gecoder 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,101 @@
1
+ module Gecode
2
+ class Model
3
+ # Specifies an integer regexp that matches +regexp+ repeated between
4
+ # +at_least+ and +at_most+ times (inclusive). If +at_most+ is
5
+ # omitted then no upper bound is placed. If both +at_least+ and
6
+ # +at_most+ are omitted then no bounds are placed.
7
+ #
8
+ # See Constraints::IntEnum::Extensional::RegexpConstraint for the
9
+ # allowed syntax of +regexp+.
10
+ def repeat(regexp, at_least = nil, at_most = nil)
11
+ unless at_least.nil? or at_least.kind_of? Fixnum
12
+ raise TypeError,
13
+ "Expected the at_least argument to be a Fixnum, got #{at_least.class}"
14
+ end
15
+ unless at_most.nil? or at_most.kind_of?(Fixnum)
16
+ raise TypeError,
17
+ "Expected the at_most argument to be a Fixnum, got #{at_most.class}"
18
+ end
19
+
20
+ reg = Constraints::Util::Extensional.parse_regexp regexp
21
+ if at_most.nil?
22
+ if at_least.nil?
23
+ reg.send '*'
24
+ else
25
+ reg.send('()', at_least)
26
+ end
27
+ else
28
+ reg.send('()', at_least, at_most)
29
+ end
30
+ end
31
+
32
+ # Matches +regexp+ repeated zero or one time (i.e. like '?' in normal
33
+ # regexps). Produces the same result as calling
34
+ #
35
+ # repeat(regexp, 0, 1)
36
+ def at_most_once(regexp)
37
+ repeat(regexp, 0, 1)
38
+ end
39
+
40
+ # Matches +regexp+ repeated at least one time (i.e. like '+' in normal
41
+ # regexps). Produces the same result as calling
42
+ #
43
+ # repeat(regexp, 1)
44
+ def at_least_once(regexp)
45
+ repeat(regexp, 1)
46
+ end
47
+
48
+ # Matches any of the specified +regexps+.
49
+ def any(*regexps)
50
+ regexps.inject(Gecode::Raw::REG.new) do |result, regexp|
51
+ result | Constraints::Util::Extensional.parse_regexp(regexp)
52
+ end
53
+ end
54
+ end
55
+
56
+ module Constraints::Util::Extensional
57
+ module_function
58
+
59
+ # Parses a regular expression over the integer domain, returning
60
+ # an instance of Gecode::REG .
61
+ #
62
+ # Pseudo-BNF of the integer regexp representation:
63
+ # regexp ::= <Fixnum> | <TrueClass> | <FalseClass> | <Gecode::Raw::REG>
64
+ # | [<regexp>, ...]
65
+ def parse_regexp(regexp)
66
+ # Check the involved types.
67
+ unless regexp.kind_of? Enumerable
68
+ regexp = [regexp]
69
+ end
70
+ regexp.to_a.flatten.each do |element|
71
+ unless element.kind_of?(Fixnum) or element.kind_of?(Gecode::Raw::REG) or
72
+ element.kind_of?(TrueClass) or element.kind_of?(FalseClass)
73
+ raise TypeError,
74
+ "Can't translate #{element.class} into integer or boolean regexp."
75
+ end
76
+ end
77
+
78
+ # Convert it into a regexp.
79
+ internal_parse_regexp(regexp)
80
+ end
81
+
82
+ private
83
+
84
+ # Recursively converts arg into an instance of Gecode::REG. It is
85
+ # assumed that arg is of kind Gecode::Raw::REG, Fixnum, TrueClass,
86
+ # FalseClass or Enumerable.
87
+ def self.internal_parse_regexp(arg)
88
+ case arg
89
+ when Gecode::Raw::REG: arg
90
+ when Fixnum: Gecode::Raw::REG.new(arg)
91
+ when TrueClass: Gecode::Raw::REG.new(1)
92
+ when FalseClass: Gecode::Raw::REG.new(0)
93
+ when Enumerable
94
+ # Recursively convert the elements of the arg.
95
+ arg.inject(Gecode::Raw::REG.new) do |regexp, element|
96
+ regexp += internal_parse_regexp(element)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -35,19 +35,7 @@ module Gecode::Constraints::Int
35
35
  # set to k then the integer variable takes value i+k exactly
36
36
  # when the variable at index i in the boolean enumration is true
37
37
  # and the rest are false.
38
- def channel(bool_enum, options = {})
39
- unless bool_enum.respond_to? :to_bool_var_array
40
- raise TypeError, 'Expected an enumration of boolean variables, got ' +
41
- "#{bool_enum.class}."
42
- end
43
-
44
- # Just provide commutivity with the boolean enum channel constraint.
45
- if @params[:negate]
46
- bool_enum.must_not.channel(@params[:lhs], options)
47
- else
48
- bool_enum.must.channel(@params[:lhs], options)
49
- end
50
- end
38
+ provide_commutivity(:channel){ |rhs, _| rhs.respond_to? :to_bool_var_array }
51
39
  end
52
40
 
53
41
  # A module that gathers the classes and modules used in channel constraints
@@ -7,23 +7,20 @@ module Gecode::Constraints::IntEnum
7
7
  raise Gecode::MissingConstraintError, 'A negated channel constraint ' +
8
8
  'is not implemented.'
9
9
  end
10
- unless enum.respond_to?(:to_int_var_array) or
11
- enum.respond_to?(:to_set_var_array)
12
- raise TypeError, "Expected int or set enum, got #{enum.class}."
10
+ unless enum.respond_to?(:to_int_var_array)
11
+ raise TypeError, "Expected int enum, got #{enum.class}."
13
12
  end
14
- if enum.respond_to? :to_set_var_array
15
- if options.has_key? :reify
16
- raise ArgumentError, 'The channel constraints does not support the ' +
17
- 'reification option.'
18
- end
19
- @params.update(Gecode::Constraints::Set::Util.decode_options(options))
20
- else
21
- @params.update(Gecode::Constraints::Util.decode_options(options))
13
+ if options.has_key? :reify
14
+ raise ArgumentError, 'The channel constraints does not support the ' +
15
+ 'reification option.'
22
16
  end
23
17
 
18
+ @params.update(Gecode::Constraints::Util.decode_options(options))
24
19
  @params.update(:rhs => enum)
25
20
  @model.add_constraint Channel::ChannelConstraint.new(@model, @params)
26
21
  end
22
+
23
+ provide_commutivity(:channel){ |rhs, _| rhs.respond_to? :to_set_var_array }
27
24
  end
28
25
 
29
26
  # A module that gathers the classes and modules used in channel constraints.
@@ -33,14 +30,12 @@ module Gecode::Constraints::IntEnum
33
30
  # enumeration of set variables. Channel constraints are used to give
34
31
  # access to multiple viewpoints when modelling.
35
32
  #
36
- # When used on two integer enumeration, the channel constraint can be
37
- # thought of as constraining the arrays to be each other's inverses. When
38
- # used with an enumeration of sets the i:th set includes the value of the
39
- # j:th integer.
33
+ # The channel constraint can be thought of as constraining the arrays to
34
+ # be each other's inverses. I.e. if the i:th value in the first enumeration
35
+ # is j, then the j:th value in the second enumeration is constrained to be
36
+ # i.
40
37
  #
41
- # Neither reification nor negation is supported. Selecting strength is only
42
- # supported when using the constraint between two integer enumerations,
43
- # it's not supported when a set enumeration is used.
38
+ # Neither reification nor negation is supported.
44
39
  #
45
40
  # == Example
46
41
  #
@@ -82,26 +77,11 @@ module Gecode::Constraints::IntEnum
82
77
  #
83
78
  # elements.must.channel positions
84
79
  #
85
- # == Example (sets)
86
- #
87
- # # +set_enum+ is constrained to channel +int_enum+.
88
- # int_enum.must.channel set_enum
89
- #
90
- # # This is another way of saying the above.
91
- # set_enum.must.channel int_enum
92
80
  class ChannelConstraint < Gecode::Constraints::Constraint
93
81
  def post
94
82
  lhs, rhs = @params.values_at(:lhs, :rhs)
95
-
96
- lhs = lhs.to_int_var_array
97
- if rhs.respond_to? :to_int_var_array
98
- # Int var array.
99
- Gecode::Raw::channel(@model.active_space, lhs, rhs.to_int_var_array,
100
- *propagation_options)
101
- else
102
- # Set var array, no propagation options.
103
- Gecode::Raw::channel(@model.active_space, lhs, rhs.to_set_var_array)
104
- end
83
+ Gecode::Raw::channel(@model.active_space, lhs.to_int_var_array,
84
+ rhs.to_int_var_array, *propagation_options)
105
85
  end
106
86
  end
107
87
  end
@@ -26,6 +26,27 @@ module Gecode::Constraints::IntEnum
26
26
  @model.add_constraint Extensional::TupleConstraint.new(@model,
27
27
  @params.update(util.decode_options(options)))
28
28
  end
29
+
30
+ # Adds a constraint that forces the enumeration to match the
31
+ # specified regular expression over the integer domain. The regular
32
+ # expression is expressed using arrays and integers. See
33
+ # IntEnum::Extensional::RegexpConstraint for more information and examples of
34
+ # such regexps.
35
+ def match(regexp, options = {})
36
+ if @params[:negate]
37
+ raise Gecode::MissingConstraintError, 'A negated regexp constraint ' +
38
+ 'is not implemented.'
39
+ end
40
+ unless options[:reify].nil?
41
+ raise ArgumentError, 'Reification is not supported by the regexp ' +
42
+ 'constraint.'
43
+ end
44
+
45
+ @params[:regexp] =
46
+ Gecode::Constraints::Util::Extensional.parse_regexp regexp
47
+ @params.update Gecode::Constraints::Util.decode_options(options)
48
+ @model.add_constraint Extensional::RegexpConstraint.new(@model, @params)
49
+ end
29
50
  end
30
51
 
31
52
  # A module that gathers the classes and modules used in extensional
@@ -60,5 +81,114 @@ module Gecode::Constraints::IntEnum
60
81
  *propagation_options)
61
82
  end
62
83
  end
84
+
85
+ # Describes a regexp constraint, which constrains the enumeration of
86
+ # integer variables to match a specified regexp in the integer
87
+ # domain. Neither negation nor reification is supported.
88
+ #
89
+ # == Regexp syntax
90
+ #
91
+ # The regular expressions are specified using arrays, integers and a
92
+ # few methods provided by Model. Arrays are used to group the
93
+ # integers in sequences that must be matched. The following array
94
+ # describes a regular expression matching a 1 followed by a 7.
95
+ #
96
+ # [1, 7]
97
+ #
98
+ # Arrays can be nested or left out when not needed. I.e. the above
99
+ # is semantically equal to
100
+ #
101
+ # [[[1], 7]]
102
+ #
103
+ # A couple of methods provided by Model are used to express patterns
104
+ # beyond mere sequences:
105
+ #
106
+ # [Model#repeat] Used for specifying patterns that include patterns
107
+ # that may be repeated a given number of times. The
108
+ # number of times to repeat a pattern can be specified
109
+ # using a lower and upper bound, but the bounds can be
110
+ # omitted to for instance allow an expression to be
111
+ # repeated any number of times.
112
+ # [Model#any] Used for specifying alternatives.
113
+ #
114
+ # Additionally Model#at_least_once and Model#at_most_once are
115
+ # provided as convenience methods.
116
+ #
117
+ # === Examples
118
+ #
119
+ # # Matches 1 followed by any number of 2s.
120
+ # [1, repeat(2)]
121
+ #
122
+ # # Semantically the same as above. It just has a bunch of
123
+ # # needless brackets thrown in.
124
+ # [[1], [repeat([2])]]
125
+ #
126
+ # # Matches 1 followed by [a 2 followed by a 3] at least two times.
127
+ # # Matches e.g. 1, 2, 3, 2, 3
128
+ # [1, repeat([2, 3], 2)]
129
+ #
130
+ # # Matches between one and two [2 followed by [at least three 1]]
131
+ # # followed by between three and four 3. Matches e.g.
132
+ # # 2, 1, 1, 1, 2, 1, 1, 1, 3, 3, 3
133
+ # [repeat([2, repeat(1, 3], 1, 2), repeat(3, 3, 4)]
134
+ #
135
+ # # Matches [1, 2 or 3] followed by 4. Matches e.g. 2, 4
136
+ # [any(1, 2, 3), 4]
137
+ #
138
+ # # Matches 0 followed by [[1 followed by 2] or [3 followed by 5]].
139
+ # # Matches e.g. 0, 1, 2 as well as 0, 3, 5
140
+ # [0, any([1, 2], [3, 5])]
141
+ #
142
+ # # Matches 0 followed by [[[1 followed by 7] at least two times]
143
+ # # or [[8, 9], at most two times]. Matches e.g.
144
+ # # 0, 1, 7, 1, 7, 1, 7 as well as 0, 8, 9
145
+ # [0, any(repeat([1, 7], 2), repeat([8, 9], 0, 2)]
146
+ #
147
+ # # Matches 0 followed by at least one 1.
148
+ # [0, at_least_once(1)]
149
+ #
150
+ # # Exactly the same as the above.
151
+ # [0, repeat(1, 1)]
152
+ #
153
+ # # Matches 0 followed by at least one [[1 followed by 7] or [3
154
+ # # followed by 2]]. Matches e.g. 0, 1, 7, 3, 2, 1, 7
155
+ # [0, at_least_once(any([1, 7], [3, 2]]
156
+ #
157
+ # # Matches 0 followed by at either [[1 followed by 7] at least once]
158
+ # # or [[3 followed by 2] at least once]. Matches e.g.
159
+ # # 0, 1, 7, 1, 7 but does _not_ match 0, 1, 7, 3, 2, 1, 7
160
+ # [0, any(at_least_once([1, 7]), at_least_once([3, 2])]
161
+ #
162
+ # # Matches 0, followed by at most one 1. Matches 0 as well as
163
+ # # 0, 1
164
+ # [0, at_most_once(1)]
165
+ #
166
+ # # Exactly the same as the above.
167
+ # [0, repeat(1, 0, 1)]
168
+ #
169
+ # == Example
170
+ #
171
+ # # Constrains the two integer variables in +numbers+ to have
172
+ # # values 1 and 7.
173
+ # numbers.must.match [1, 7]
174
+ #
175
+ # # Constrains the integer variables in +numbers+ to contain the
176
+ # # value 47 followed by 11, with all other values set to -1.
177
+ # numbers.must.match [repeat(-1), 47, 11, repeat(-1)]
178
+ #
179
+ # # Constrains exactly three of the integer variables in +numbers+ to
180
+ # # contain 47 or 11, each followed by at least two
181
+ # # variables set to -1. All other variables are constrained to
182
+ # # equal -1.
183
+ # numbers.must.match repeat([repeat(-1), any(11, 47),
184
+ # repeat(-1, 2)], 3, 3)
185
+ #
186
+ class RegexpConstraint < Gecode::Constraints::Constraint
187
+ def post
188
+ lhs, regexp = @params.values_at(:lhs, :regexp)
189
+ Gecode::Raw::extensional(@model.active_space, lhs.to_int_var_array,
190
+ regexp, *propagation_options)
191
+ end
192
+ end
63
193
  end
64
194
  end
@@ -0,0 +1,54 @@
1
+ module Gecode::Constraints::Set
2
+ class Expression
3
+ # Adds a channel constraint on the set variable with the specified enum of
4
+ # boolean variables.
5
+ def channel(bool_enum, options = {})
6
+ if @params[:negate]
7
+ raise Gecode::MissingConstraintError, 'A negated channel constraint ' +
8
+ 'is not implemented.'
9
+ end
10
+ if options.has_key? :reify
11
+ raise ArgumentError, 'The channel constraint does not support the ' +
12
+ 'reification option.'
13
+ end
14
+ unless bool_enum.respond_to? :to_bool_var_array
15
+ raise TypeError, 'Expected an enum of bool variables, ' +
16
+ "got #{bool_enum.class}."
17
+ end
18
+
19
+ @params.update(:rhs => bool_enum)
20
+ @params.update Gecode::Constraints::Set::Util.decode_options(options)
21
+ @model.add_constraint Channel::ChannelConstraint.new(@model, @params)
22
+ end
23
+ end
24
+
25
+ # A module that gathers the classes and modules used in channel constraints
26
+ # involving one set variable and a boolean enum.
27
+ module Channel #:nodoc:
28
+ # Describes a channel constraint that "channels" a set variable and an
29
+ # enumerations of boolean variables. This constrains the set variable to
30
+ # include value i exactly when the variable at index i in the boolean
31
+ # enumeration is true.
32
+ #
33
+ # Neither reification nor negation is supported. The boolean enum and set
34
+ # can be interchanged.
35
+ #
36
+ # == Examples
37
+ #
38
+ # # Constrains the enumeration of boolean variables called +bools+ to at
39
+ # # least have the first and third variables set to true
40
+ # set.must_be.superset_of [0, 2]
41
+ # set.must.channel bools
42
+ #
43
+ # # An alternative way of writing the above.
44
+ # set.must_be.superset_of [0, 2]
45
+ # bools.must.channel set
46
+ class ChannelConstraint < Gecode::Constraints::Constraint
47
+ def post
48
+ lhs, rhs = @params.values_at(:lhs, :rhs)
49
+ Gecode::Raw::channel(@model.active_space, rhs.to_bool_var_array,
50
+ lhs.bind)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -2,16 +2,47 @@ module Gecode::Constraints::SetEnum
2
2
  class Expression
3
3
  # Posts a channel constraint on the variables in the enum with the specified
4
4
  # int enum.
5
- def channel(enum)
5
+ def channel(enum, options = {})
6
6
  unless enum.respond_to? :to_int_var_array
7
7
  raise TypeError, "Expected integer variable enum, for #{enum.class}."
8
8
  end
9
-
10
- # Just provide commutativity to the corresponding int enum constraint.
11
9
  if @params[:negate]
12
- enum.must_not.channel(@params[:lhs])
13
- else
14
- enum.must.channel(@params[:lhs])
10
+ raise Gecode::MissingConstraintError, 'A negated channel constraint ' +
11
+ 'is not implemented.'
12
+ end
13
+ if options.has_key? :reify
14
+ raise ArgumentError, 'The channel constraints does not support the ' +
15
+ 'reification option.'
16
+ end
17
+
18
+ @params.update(Gecode::Constraints::Set::Util.decode_options(options))
19
+ @params.update(:rhs => enum)
20
+ @model.add_constraint Channel::IntChannelConstraint.new(@model, @params)
21
+ end
22
+ end
23
+
24
+ # A module that gathers the classes and modules used in channel constraints.
25
+ module Channel #:nodoc:
26
+ # Describes a channel constraint which "channels" one enumeration of
27
+ # integer variables with one enumeration of set variables. The i:th set
28
+ # in the enumeration of set variables is constrainde to includes the value
29
+ # of the j:th integer variable.
30
+ #
31
+ # Neither reification nor negation is supported.
32
+ #
33
+ # == Examples
34
+ #
35
+ # # +set_enum+ is constrained to channel +int_enum+.
36
+ # int_enum.must.channel set_enum
37
+ #
38
+ # # This is another way of saying the above.
39
+ # set_enum.must.channel int_enum
40
+ #
41
+ class IntChannelConstraint < Gecode::Constraints::Constraint
42
+ def post
43
+ lhs, rhs = @params.values_at(:lhs, :rhs)
44
+ Gecode::Raw::channel(@model.active_space, rhs.to_int_var_array,
45
+ lhs.to_set_var_array)
15
46
  end
16
47
  end
17
48
  end
@@ -75,3 +75,4 @@ require 'gecoder/interface/constraints/set/relation'
75
75
  require 'gecoder/interface/constraints/set/cardinality'
76
76
  require 'gecoder/interface/constraints/set/connection'
77
77
  require 'gecoder/interface/constraints/set/operation'
78
+ require 'gecoder/interface/constraints/set/channel'
@@ -271,6 +271,43 @@ module Gecode
271
271
 
272
272
  private
273
273
 
274
+ # Provides commutivity for the constraint with the specified method name.
275
+ # If the method with the specified method name is called with something
276
+ # that, when given to the block, evaluates to true, then the constraint
277
+ # will be called on the right hand side with the left hand side as
278
+ # argument.
279
+ #
280
+ # The original constraint method is assumed to take two arguments: a
281
+ # right hand side and a hash of options.
282
+ def self.provide_commutivity(constraint_name, &block)
283
+ unique_id = constraint_name.to_sym.to_i
284
+ pre_alias_method_name = 'pre_commutivity_' << unique_id.to_s
285
+ if method_defined? constraint_name
286
+ alias_method pre_alias_method_name, constraint_name
287
+ end
288
+
289
+ module_eval <<-end_code
290
+ @@commutivity_check_#{unique_id} = block
291
+ def #{constraint_name}(rhs, options = {})
292
+ if @@commutivity_check_#{unique_id}.call(rhs, options)
293
+ if @params[:negate]
294
+ rhs.must_not.method(:#{constraint_name}).call(
295
+ @params[:lhs], options)
296
+ else
297
+ rhs.must.method(:#{constraint_name}).call(
298
+ @params[:lhs], options)
299
+ end
300
+ else
301
+ if self.class.method_defined? :#{pre_alias_method_name}
302
+ #{pre_alias_method_name}(rhs, options)
303
+ else
304
+ raise TypeError, \"Unexpected argument type \#{rhs.class}.\"
305
+ end
306
+ end
307
+ end
308
+ end_code
309
+ end
310
+
274
311
  # Creates aliases for any defined comparison methods.
275
312
  def self.alias_comparison_methods
276
313
  Gecode::Constraints::Util::COMPARISON_ALIASES.each_pair do |orig, aliases|
@@ -472,3 +509,4 @@ require 'gecoder/interface/constraints/bool_var_constraints'
472
509
  require 'gecoder/interface/constraints/bool_enum_constraints'
473
510
  require 'gecoder/interface/constraints/set_var_constraints'
474
511
  require 'gecoder/interface/constraints/set_enum_constraints'
512
+ require 'gecoder/interface/constraints/extensional_regexp'