gecoder 0.8.2 → 0.8.3

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.
Files changed (38) hide show
  1. data/CHANGES +14 -0
  2. data/ext/gecoder.cpp +181 -0
  3. data/ext/gecoder.h +94 -0
  4. data/ext/vararray.cpp +3 -3
  5. data/lib/gecoder/bindings/bindings.rb +104 -46
  6. data/lib/gecoder/interface/binding_changes.rb +1 -301
  7. data/lib/gecoder/interface/branch.rb +15 -11
  8. data/lib/gecoder/interface/constraints/bool/boolean.rb +56 -52
  9. data/lib/gecoder/interface/constraints/bool/channel.rb +1 -16
  10. data/lib/gecoder/interface/constraints/bool_enum/channel.rb +13 -8
  11. data/lib/gecoder/interface/constraints/bool_enum/extensional.rb +48 -0
  12. data/lib/gecoder/interface/constraints/extensional_regexp.rb +101 -0
  13. data/lib/gecoder/interface/constraints/int/channel.rb +1 -13
  14. data/lib/gecoder/interface/constraints/int_enum/channel.rb +15 -35
  15. data/lib/gecoder/interface/constraints/int_enum/extensional.rb +130 -0
  16. data/lib/gecoder/interface/constraints/set/channel.rb +54 -0
  17. data/lib/gecoder/interface/constraints/set_enum/channel.rb +37 -6
  18. data/lib/gecoder/interface/constraints/set_var_constraints.rb +1 -0
  19. data/lib/gecoder/interface/constraints.rb +38 -0
  20. data/lib/gecoder/interface/model.rb +110 -85
  21. data/lib/gecoder/interface/variables.rb +3 -21
  22. data/lib/gecoder/version.rb +1 -1
  23. data/specs/branch.rb +16 -1
  24. data/specs/constraints/bool_enum_relation.rb +6 -6
  25. data/specs/constraints/boolean.rb +31 -25
  26. data/specs/constraints/channel.rb +102 -4
  27. data/specs/constraints/extensional.rb +185 -2
  28. data/specs/constraints/reification_sugar.rb +2 -46
  29. data/specs/model.rb +85 -7
  30. data/tasks/dependencies.txt +1 -0
  31. data/vendor/rust/rust/class.rb +33 -35
  32. data/vendor/rust/rust/templates/ClassDeclarations.rusttpl +1 -1
  33. data/vendor/rust/rust/templates/CxxClassDefinitions.rusttpl +10 -1
  34. metadata +185 -184
  35. data/example/raw_bindings.rb +0 -44
  36. data/ext/missing.cpp +0 -328
  37. data/ext/missing.h +0 -120
  38. data/specs/binding_changes.rb +0 -76
@@ -1,309 +1,9 @@
1
- # This file adds a small layer on top of the bindings. It alters the allocation
2
- # of variables so that a single array is allocated in each space which is then
3
- # used to store variable. The variables themselves are not directly returned,
4
- # rather they are represented as the index in that store, which allows the
5
- # variable to be retrieved back given a space.
6
- #
7
- # This layer should be moved to the C++ side instead when possible for better
8
- # performance.
1
+ # This file adds a small layer on top of the bindings.
9
2
  module GecodeRaw #:nodoc: all
10
3
  class Space
11
- # Creates the specified number of integer variables in the space with the
12
- # specified domain. Returns the indices with which they can then be
13
- # accessed using int_var. The domain can be given as a Range (trated
14
- # specially) or as another enum.
15
- def new_int_vars(domain, count = 1)
16
- int_var_store.new_vars(domain, count)
17
- end
18
-
19
- # Gets the int variable with the specified index, nil if none exists.
20
- def int_var(index)
21
- int_var_store[index]
22
- end
23
-
24
- # Creates the specified number of boolean variables in the space. Returns
25
- # the indices with which they can then be accessed using bool_var.
26
- def new_bool_vars(count = 1)
27
- bool_var_store.new_vars(count)
28
- end
29
-
30
- # Gets the bool variable with the specified index, nil if none exists.
31
- def bool_var(index)
32
- bool_var_store[index]
33
- end
34
-
35
- # Creates the specified number of set variables in the space with the
36
- # specified domain for greatest lower bound and least upper bound
37
- # (specified as either a range or enum). A range for the allowed
38
- # cardinality of the set can also be specified, if none is specified, or
39
- # nil is given, then the default range (anything) will be used. Returns
40
- # the indices with which they can then be accessed using set_var.
41
- def new_set_vars(*vars)
42
- set_var_store.new_vars(*vars)
43
- end
44
-
45
- # Gets the set variable with the specified index, nil if none exists.
46
- def set_var(index)
47
- set_var_store[index]
48
- end
49
-
50
4
  # Used by Gecode during BAB-search.
51
5
  def constrain(best_so_far_space)
52
6
  Gecode::Model.constrain(self, best_so_far_space)
53
7
  end
54
-
55
- # Refreshes the underlying stores used by the space.
56
- def refresh
57
- @int_var_store = nil
58
- @bool_var_store = nil
59
- @set_var_store = nil
60
- end
61
-
62
- private
63
-
64
- # Retrieves the store used for integer variables. Creates one if none
65
- # exists.
66
- def int_var_store
67
- # TODO: caching interferes with the variable creation during BAB-search,
68
- # find out why.
69
- #if @int_var_store.nil?
70
- @int_var_store = Gecode::Util::IntVarStore.new(self)
71
- #end
72
- return @int_var_store
73
- end
74
-
75
- # Retrieves the store used for boolean variables. Creates one if none
76
- # exists.
77
- def bool_var_store
78
- #if @bool_var_store.nil?
79
- @bool_var_store = Gecode::Util::BoolVarStore.new(self)
80
- #end
81
- return @bool_var_store
82
- end
83
-
84
- # Retrieves the store used for set variables. Creates one if none exists.
85
- def set_var_store
86
- #if @set_var_store.nil?
87
- @set_var_store = Gecode::Util::SetVarStore.new(self)
88
- #end
89
- return @set_var_store
90
- end
91
- end
92
- end
93
-
94
- module Gecode
95
- # Various utility (mainly used to change the behavior of the raw bindings).
96
- module Util #:nodoc: all
97
- # Provides common methods to the variable stores. The stores must provide
98
- # @next_index, @var_array, @size, ARRAY_IDENTIFIER and #new_storage_array .
99
- module VarStoreMethods
100
- # Returns the int var with the specified index, nil if none exists.
101
- def [](index)
102
- if index < 0 or index >= @next_index
103
- return nil
104
- end
105
- return @var_array.at(index)
106
- end
107
-
108
- private
109
-
110
- # Grows the store to the new size.
111
- def grow(new_size)
112
- @var_array.enlargeArray(@space, new_size - @size)
113
- @size = new_size
114
- end
115
- end
116
-
117
- # A store in which int variables are created and stored.
118
- class IntVarStore
119
- # Design note: The store used to double its size when it needed to grow
120
- # leaving unallocated slots (in rev 16). This was changed to only growing
121
- # the amount of space needed because the additional information about which
122
- # slot is the next unallocated one could not be encoded without changes to
123
- # the bindings (and without that information we can not deduce the store
124
- # from the new copy of space). So for additional performance the bindings
125
- # should grow the array more than needed (when this is moved to the bindings).
126
-
127
- include VarStoreMethods
128
-
129
- private
130
-
131
- # A string that identifies the array used by the store.
132
- ARRAY_IDENTIFIER = 'int_array'
133
-
134
- public
135
-
136
- # Creates a store for the specified space with the specified capacit.
137
- def initialize(space)
138
- @space = space
139
-
140
- @var_array = space.int_var_array(ARRAY_IDENTIFIER)
141
- if @var_array.nil?
142
- # Create a new one.
143
- @var_array = new_storage_array(0)
144
- end
145
-
146
- @size = @var_array.size
147
- @next_index = @size
148
- end
149
-
150
- # Creates the specified number of integer variables in the space with the
151
- # specified domain. Returns the indices with which they can then be
152
- # accessed using int_var. The domain can be given as a Range (trated
153
- # specially) or as another enum.
154
- def new_vars(domain, count = 1)
155
- grow(@next_index + count) # See the design note for more information.
156
- count.times do |i|
157
- if domain.kind_of? Range
158
- domain_params = [domain.first, domain.last]
159
- elsif domain.kind_of? Enumerable
160
- arr = domain.to_a
161
- domain_params = [Gecode::Raw::IntSet.new(arr, arr.size)]
162
- else
163
- raise TypeError, "Expected Enumerable, got #{domain.class}."
164
- end
165
-
166
- @var_array[@next_index] = Gecode::Raw::IntVar.new(@space,
167
- *domain_params)
168
- @next_index += 1
169
- end
170
-
171
- ((@next_index - count)...@next_index).to_a
172
- end
173
-
174
- # Returns the int var with the specified index, nil if none exists.
175
- def [](index)
176
- if index < 0 or index >= @next_index
177
- return nil
178
- end
179
- return @var_array.at(index)
180
- end
181
-
182
- private
183
-
184
- # Creates a new storage array for int variables.
185
- def new_storage_array(new_size)
186
- arr = Gecode::Raw::IntVarArray.new(@space, new_size)
187
- @space.own(arr, ARRAY_IDENTIFIER)
188
- return arr
189
- end
190
- end
191
-
192
- # A store in which int variables are created and stored.
193
- class BoolVarStore
194
- # TODO: can we refactor this better seeing as IntVarStore and BoolVarStore
195
- # are similar?
196
-
197
- include VarStoreMethods
198
-
199
- private
200
-
201
- # A string that identifies the array used by the store.
202
- ARRAY_IDENTIFIER = 'bool_array'
203
-
204
- public
205
-
206
- # Creates a store for the specified space with the specified capacit.
207
- def initialize(space)
208
- @space = space
209
-
210
- @var_array = space.bool_var_array(ARRAY_IDENTIFIER)
211
- if @var_array.nil?
212
- # Create a new one.
213
- @var_array = new_storage_array(0)
214
- end
215
-
216
- @size = @var_array.size
217
- @next_index = @size
218
- end
219
-
220
- # Creates the specified number of new bool variables. Returns the indices
221
- # of the created variables as an array.
222
- def new_vars(count = 1)
223
- grow(@next_index + count) # See the design note for more information.
224
- count.times do |i|
225
- @var_array[@next_index] = Gecode::Raw::BoolVar.new(@space, 0, 1)
226
- @next_index += 1
227
- end
228
-
229
- ((@next_index - count)...@next_index).to_a
230
- end
231
-
232
- private
233
-
234
- # Creates a new storage array for bool variables.
235
- def new_storage_array(new_size)
236
- arr = Gecode::Raw::BoolVarArray.new(@space, new_size)
237
- @space.own(arr, ARRAY_IDENTIFIER)
238
- return arr
239
- end
240
- end
241
-
242
- # A store in which int variables are created and stored.
243
- class SetVarStore
244
- include VarStoreMethods
245
-
246
- private
247
-
248
- # A string that identifies the array used by the store.
249
- ARRAY_IDENTIFIER = 'set_array'
250
-
251
- public
252
-
253
- # Creates a store for the specified space with the specified capacit.
254
- def initialize(space)
255
- @space = space
256
-
257
- @var_array = space.set_var_array(ARRAY_IDENTIFIER)
258
- if @var_array.nil?
259
- # Create a new one.
260
- @var_array = new_storage_array(0)
261
- end
262
-
263
- @size = @var_array.size
264
- @next_index = @size
265
- end
266
-
267
- # Creates the specified number of set variables in the space with the
268
- # specified domain for greatest lower bound and least upper bound
269
- # (specified as either a range or enum). A range for the allowed
270
- # cardinality of the set can also be specified, if none is specified, or
271
- # nil is given, then the default range (anything) will be used. Returns
272
- # the indices with which they can then be accessed using set_var.
273
- def new_vars(glb_domain, lub_domain, cardinality_range = nil, count = 1)
274
- grow(@next_index + count) # See the design note for more information.
275
-
276
- if cardinality_range.nil?
277
- cardinality_range = 0..Gecode::Raw::SetLimits::CARD
278
- end
279
-
280
- params = [@space]
281
- params << domain_to_args(glb_domain)
282
- params << domain_to_args(lub_domain)
283
- params << cardinality_range.first << cardinality_range.last
284
- count.times do |i|
285
- @var_array[@next_index] = Gecode::Raw::SetVar.new(*params.flatten)
286
- @next_index += 1
287
- end
288
-
289
- ((@next_index - count)...@next_index).to_a
290
- end
291
-
292
- private
293
-
294
- # Transforms a lub or glb domain given as a fixnum, range or enumeration
295
- # into one or more parameters that describe the domain to
296
- # Gecode::Raw::SetVar .
297
- def domain_to_args(domain)
298
- Gecode::Constraints::Util.constant_set_to_int_set(domain)
299
- end
300
-
301
- # Creates a new storage array for bool variables.
302
- def new_storage_array(new_size)
303
- arr = Gecode::Raw::SetVarArray.new(@space, new_size)
304
- @space.own(arr, ARRAY_IDENTIFIER)
305
- return arr
306
- end
307
- end
308
8
  end
309
9
  end
@@ -1,13 +1,14 @@
1
1
  module Gecode
2
2
  class Model
3
- # Specifies which variables that should be branched on. One can optionally
4
- # also select which of the variables that should be used first with the
5
- # :variable option and which value in that variable's domain that should be
6
- # used with the :value option. If nothing is specified then :variable uses
7
- # :none and value uses :min.
3
+ # Specifies which variables that should be branched on (given as an
4
+ # enum of variables or as a single variable). One can optionally
5
+ # also select which of the variables that should be used first with
6
+ # the :variable option and which value in that variable's domain
7
+ # that should be used with the :value option. If nothing is
8
+ # specified then :variable uses :none and value uses :min.
8
9
  #
9
- # The following values can be used with :variable for integer and boolean
10
- # enums:
10
+ # The following values can be used with :variable for integer and
11
+ # boolean enums:
11
12
  # [:none] The first unassigned variable.
12
13
  # [:smallest_min] The one with the smallest minimum.
13
14
  # [:largest_min] The one with the largest minimum.
@@ -58,10 +59,13 @@ module Gecode
58
59
  # elements
59
60
  #
60
61
  # The following values can be used with :value set enums:
61
- # enums:
62
62
  # [:min] Selects the smallest value in the unknown part of the set.
63
63
  # [:max] Selects the largest value in the unknown part of the set.
64
64
  def branch_on(variables, options = {})
65
+ if variables.respond_to? :bind
66
+ variables = wrap_enum [variables]
67
+ end
68
+
65
69
  if variables.respond_to? :to_int_var_array or
66
70
  variables.respond_to? :to_bool_var_array
67
71
  add_branch(variables, options, Constants::BRANCH_INT_VAR_CONSTANTS,
@@ -78,8 +82,8 @@ module Gecode
78
82
 
79
83
  # This is a hack to get RDoc to ignore the constants.
80
84
  module Constants #:nodoc:
81
- # Maps the names of the supported variable branch strategies for integer and
82
- # booleans to the corresponding constant in Gecode.
85
+ # Maps the names of the supported variable branch strategies for
86
+ # integer and booleans to the corresponding constant in Gecode.
83
87
  BRANCH_INT_VAR_CONSTANTS = {
84
88
  :none => Gecode::Raw::INT_VAR_NONE,
85
89
  :smallest_min => Gecode::Raw::INT_VAR_MIN_MIN,
@@ -149,4 +153,4 @@ module Gecode
149
153
  end
150
154
  end
151
155
  end
152
- end
156
+ end
@@ -26,16 +26,15 @@ module Gecode
26
26
  # Describes a boolean expression (following after must*).
27
27
  class Expression #:nodoc:
28
28
  def ==(expression, options = {})
29
- unless expression.kind_of?(ExpressionTree) or
30
- expression.kind_of?(Gecode::FreeBoolVar) or
31
- expression.kind_of?(TrueClass) or expression.kind_of?(FalseClass) or
32
- expression.respond_to?(:to_minimodel_lin_exp)
33
- raise TypeError, 'Invalid right hand side of boolean equation.'
29
+ if expression.kind_of? Gecode::Constraints::Int::Linear::ExpressionTree
30
+ return expression.must == @params[:lhs]
31
+ end
32
+ unless expression.respond_to? :to_minimodel_bool_expr
33
+ expression = Constraints::Bool::ExpressionNode.new(expression, @model)
34
34
  end
35
-
36
35
  @params.update Gecode::Constraints::Util.decode_options(options)
37
- @model.add_constraint BooleanConstraint.new(@model,
38
- @params.update(:rhs => expression))
36
+ @params.update(:lhs => @params[:lhs], :rhs => expression)
37
+ @model.add_constraint BooleanConstraint.new(@model, @params)
39
38
  end
40
39
  alias_comparison_methods
41
40
 
@@ -47,14 +46,14 @@ module Gecode
47
46
  end
48
47
 
49
48
  # Constrains the boolean expression to be true.
50
- def true
51
- @params.update Gecode::Constraints::Util.decode_options({})
49
+ def true(options = {})
50
+ @params.update Gecode::Constraints::Util.decode_options(options)
52
51
  @model.add_constraint BooleanConstraint.new(@model,
53
52
  @params.update(:rhs => true))
54
53
  end
55
54
 
56
55
  # Constrains the boolean expression to be false.
57
- def false
56
+ def false(options = {})
58
57
  @params[:negate] = !@params[:negate]
59
58
  self.true
60
59
  end
@@ -136,32 +135,37 @@ module Gecode
136
135
  lhs, rhs, negate, reif_var =
137
136
  @params.values_at(:lhs, :rhs, :negate, :reif)
138
137
  space = (lhs.model || rhs.model).active_space
139
-
140
- # TODO: It should be possible to reduce the number of necessary
141
- # variables and constraints a bit by altering the way that the top node
142
- # is posted, using its constraint for reification etc when possible.
143
-
144
- if rhs.respond_to? :bind
138
+
139
+ if lhs.kind_of?(Gecode::FreeBoolVar)
140
+ lhs = Constraints::Bool::ExpressionNode.new(lhs, @model)
141
+ end
142
+
143
+ bot_eqv = Gecode::Raw::IRT_EQ
144
+ bot_xor = Gecode::Raw::IRT_NQ
145
+
146
+ if rhs.respond_to? :to_minimodel_bool_expr
145
147
  if reif_var.nil?
146
- Gecode::Raw::rel(space, lhs.bind, Gecode::Raw::BOT_EQV, rhs.bind,
147
- (!negate ? 1 : 0), *propagation_options)
148
+ tree = ExpressionTree.new(lhs,
149
+ Gecode::Raw::MiniModel::BoolExpr::NT_EQV, rhs)
150
+ tree.to_minimodel_bool_expr.post(space, !negate,
151
+ *propagation_options)
148
152
  else
149
- if negate
150
- Gecode::Raw::rel(space, lhs.bind, Gecode::Raw::BOT_XOR, rhs.bind,
151
- reif_var.bind, *propagation_options)
152
- else
153
- Gecode::Raw::rel(space, lhs.bind, Gecode::Raw::BOT_EQV, rhs.bind,
154
- reif_var.bind, *propagation_options)
155
- end
153
+ tree = ExpressionTree.new(lhs,
154
+ Gecode::Raw::MiniModel::BoolExpr::NT_EQV, rhs)
155
+ var = tree.to_minimodel_bool_expr.post(space, *propagation_options)
156
+ Gecode::Raw::rel(space, var, (negate ? bot_xor : bot_eqv),
157
+ reif_var.bind, *propagation_options)
156
158
  end
157
159
  else
158
160
  should_hold = !negate & rhs
159
161
  if reif_var.nil?
160
- Gecode::Raw::MiniModel::BoolExpr.new(lhs.bind).post(space,
161
- should_hold, *propagation_options)
162
+ lhs.to_minimodel_bool_expr.post(space, should_hold,
163
+ *propagation_options)
162
164
  else
163
- Gecode::Raw::rel(space, lhs.bind, Gecode::Raw::BOT_EQV,
164
- reif_var.bind, (should_hold ? 1 : 0), *propagation_options)
165
+ var = lhs.to_minimodel_bool_expr.post(space, *propagation_options)
166
+ Gecode::Raw::rel(space, var,
167
+ (should_hold ? bot_eqv : bot_xor),
168
+ reif_var.bind, *propagation_options)
165
169
  end
166
170
  end
167
171
  end
@@ -177,10 +181,10 @@ module Gecode
177
181
  # Maps the names of the methods to the corresponding bool constraint in
178
182
  # Gecode.
179
183
  OPERATION_TYPES = {
180
- :| => Gecode::Raw::BOT_OR,
181
- :& => Gecode::Raw::BOT_AND,
182
- :^ => Gecode::Raw::BOT_XOR,
183
- :implies => Gecode::Raw::BOT_IMP
184
+ :| => Gecode::Raw::MiniModel::BoolExpr::NT_OR,
185
+ :& => Gecode::Raw::MiniModel::BoolExpr::NT_AND,
186
+ :^ => Gecode::Raw::MiniModel::BoolExpr::NT_XOR,
187
+ :implies => Gecode::Raw::MiniModel::BoolExpr::NT_IMP
184
188
  }
185
189
 
186
190
  public
@@ -191,13 +195,7 @@ module Gecode
191
195
  unless expression.kind_of? ExpressionTree
192
196
  expression = ExpressionNode.new(expression)
193
197
  end
194
- ExpressionTree.new(self, expression) do |model, var1, var2|
195
- new_var = model.bool_var
196
- Gecode::Raw::rel(model.active_space, var1.bind, #{operation},
197
- var2.bind, new_var.bind, Gecode::Raw::ICL_DEF,
198
- Gecode::Raw::PK_DEF)
199
- new_var
200
- end
198
+ ExpressionTree.new(self, #{operation}, expression)
201
199
  end
202
200
  end_code
203
201
  end
@@ -216,17 +214,19 @@ module Gecode
216
214
  class ExpressionTree #:nodoc:
217
215
  include OperationMethods
218
216
 
219
- # Constructs a new expression with the specified nodes. The proc should
220
- # take a model followed by two variables and return a new variable.
221
- def initialize(left_tree, right_tree, &block)
217
+ # Constructs a new expression with the specified binary operation
218
+ # applied to the specified trees.
219
+ def initialize(left_tree, operation, right_tree)
222
220
  @left = left_tree
221
+ @operation = operation
223
222
  @right = right_tree
224
- @bind_proc = block
225
223
  end
226
224
 
227
- # Returns a bound boolean variable representing the expression.
228
- def bind
229
- @bind_proc.call(model, @left, @right).bind
225
+ # Returns a MiniModel boolean expression representing the tree.
226
+ def to_minimodel_bool_expr
227
+ Gecode::Raw::MiniModel::BoolExpr.new(
228
+ @left.to_minimodel_bool_expr, @operation,
229
+ @right.to_minimodel_bool_expr)
230
230
  end
231
231
 
232
232
  # Fetches the space that the expression's variables is in.
@@ -242,14 +242,18 @@ module Gecode
242
242
  attr :model
243
243
 
244
244
  def initialize(value, model = nil)
245
+ unless value.kind_of?(Gecode::FreeBoolVar)
246
+ raise TypeError, 'Invalid type used in boolean equation: ' +
247
+ "#{value.class}."
248
+ end
245
249
  @value = value
246
250
  @model = model
247
251
  end
248
252
 
249
- # Returns a bound boolean variable representing the expression.
250
- def bind
251
- @value.bind
253
+ # Returns a MiniModel boolean expression representing the tree.
254
+ def to_minimodel_bool_expr
255
+ Gecode::Raw::MiniModel::BoolExpr.new(@value.bind)
252
256
  end
253
257
  end
254
258
  end
255
- end
259
+ end
@@ -1,23 +1,8 @@
1
1
  module Gecode::Constraints::Bool
2
2
  class Expression
3
- alias_method :pre_channel_equals, :==
4
-
5
3
  # Constrains the boolean variable to be equal to the specified integer
6
4
  # variable.
7
- def ==(int, options = {})
8
- unless @params[:lhs].kind_of?(Gecode::FreeBoolVar) and
9
- int.kind_of?(Gecode::FreeIntVar)
10
- return pre_channel_equals(int, options)
11
- end
12
-
13
- # Provide commutivity to the corresponding int variable constraint.
14
- if @params[:negate]
15
- int.must_not.equal(@params[:lhs], options)
16
- else
17
- int.must.equal(@params[:lhs], options)
18
- end
19
- end
20
-
5
+ provide_commutivity(:==){ |rhs, _| rhs.kind_of?(Gecode::FreeIntVar) }
21
6
  alias_comparison_methods
22
7
  end
23
8
  end
@@ -6,7 +6,7 @@ module Gecode::Constraints::BoolEnum
6
6
  #
7
7
  # [:offset] Specifies an offset for the integer variable. If the offset is
8
8
  # set to k then the integer variable takes value i+k exactly
9
- # when the variable at index i in the boolean enumration is true
9
+ # when the variable at index i in the boolean enumeration is true
10
10
  # and the rest are false.
11
11
  def channel(int_var, options = {})
12
12
  if @params[:negate]
@@ -25,28 +25,33 @@ module Gecode::Constraints::BoolEnum
25
25
  @params.update(Gecode::Constraints::Util.decode_options(options))
26
26
  @model.add_constraint Channel::ChannelConstraint.new(@model, @params)
27
27
  end
28
+
29
+ # Adds a channel constraint on the variables in the enum with the specified
30
+ # set variable.
31
+ provide_commutivity(:channel){ |rhs, _| rhs.kind_of? Gecode::FreeSetVar }
28
32
  end
29
33
 
30
34
  # A module that gathers the classes and modules used in channel constraints
31
35
  # involving one boolean enum and one integer variable.
32
36
  module Channel #:nodoc:
33
- # Describes a channel constraint that "channels" an enumerations of
37
+ # Describes a channel constraint that "channels" an enumeration of
34
38
  # boolean variables with an integer variable. This constrains the integer
35
39
  # variable to take value i exactly when the variable at index i in the
36
40
  # boolean enumeration is true and the others are false.
37
41
  #
38
- # Neither reification nor negation is supported.
42
+ # Neither reification nor negation is supported. The int variable
43
+ # and the enumeration can be interchanged.
39
44
  #
40
45
  # == Examples
41
46
  #
42
- # # Constrains the enumeration called +selected_option+ to be false in the
47
+ # # Constrains the enumeration called +option_is_selected+ to be false in the
43
48
  # # first four positions and have exactly one true variable in the other.
44
- # selected_option.must.channel selected_option_index
49
+ # option_is_selected.must.channel selected_option_index
45
50
  # selected_option_index.must_be > 3
46
51
  #
47
- # # Constrains the enumeration called +selected_option+ to be false in the
52
+ # # Constrains the enumeration called +option_is_selected+ to be false in the
48
53
  # # first five positions and have exactly one true variable in the other.
49
- # selected_option.must.channel(selected_option_index, :offset => 1)
54
+ # selected_option_index.must.channel(option_is_selected, :offset => 1)
50
55
  # selected_option_index.must_be > 3
51
56
  class ChannelConstraint < Gecode::Constraints::Constraint
52
57
  def post
@@ -56,4 +61,4 @@ module Gecode::Constraints::BoolEnum
56
61
  end
57
62
  end
58
63
  end
59
- end
64
+ end
@@ -25,6 +25,27 @@ module Gecode::Constraints::BoolEnum
25
25
  @model.add_constraint Extensional::TupleConstraint.new(@model,
26
26
  @params.update(Gecode::Constraints::Util.decode_options(options)))
27
27
  end
28
+
29
+ # Adds a constraint that forces the enumeration to match the
30
+ # specified regular expression over the boolean domain. The regular
31
+ # expression is expressed using arrays and boolean values (or
32
+ # integers). See BoolEnum::Extensional::RegexpConstraint for more information
33
+ # and examples of such regexps.
34
+ def match(regexp, options = {})
35
+ if @params[:negate]
36
+ raise Gecode::MissingConstraintError, 'A negated regexp constraint ' +
37
+ 'is not implemented.'
38
+ end
39
+ unless options[:reify].nil?
40
+ raise ArgumentError, 'Reification is not supported by the regexp ' +
41
+ 'constraint.'
42
+ end
43
+
44
+ @params[:regexp] =
45
+ Gecode::Constraints::Util::Extensional.parse_regexp regexp
46
+ @params.update Gecode::Constraints::Util.decode_options(options)
47
+ @model.add_constraint Extensional::RegexpConstraint.new(@model, @params)
48
+ end
28
49
  end
29
50
 
30
51
  # A module that gathers the classes and modules used in extensional
@@ -60,5 +81,32 @@ module Gecode::Constraints::BoolEnum
60
81
  *propagation_options)
61
82
  end
62
83
  end
84
+
85
+ # Describes a regexp constraint, which constrains the enumeration of
86
+ # boolean variables to match a specified regexp in the boolean
87
+ # domain. Neither negation nor reification is supported.
88
+ #
89
+ # The regular expressions are specified as described in
90
+ # IntEnum::Extensional::RegexpConstraint but true and false can be
91
+ # used instead of integers.
92
+ #
93
+ # == Example
94
+ #
95
+ # # Constrains the two boolean variables in +bools+ to be false
96
+ # # and true respectively.
97
+ # bools.must.match [false, true]
98
+ #
99
+ # # Constrains the boolean variables in +bools+ to be false,
100
+ # # except for three consecutive variables which should be true
101
+ # # followed by false followed by true.
102
+ # bools.must.match [repeat(false), true, false, true, repeat(false)]]
103
+ #
104
+ class RegexpConstraint < Gecode::Constraints::Constraint
105
+ def post
106
+ lhs, regexp = @params.values_at(:lhs, :regexp)
107
+ Gecode::Raw::extensional(@model.active_space, lhs.to_bool_var_array,
108
+ regexp, *propagation_options)
109
+ end
110
+ end
63
111
  end
64
112
  end