gecoder 0.8.3 → 0.9.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.
- data/CHANGES +15 -0
- data/README +6 -2
- data/example/equation_system.rb +15 -0
- data/example/magic_sequence.rb +7 -7
- data/example/money.rb +36 -0
- data/example/queens.rb +7 -8
- data/example/send_most_money.rb +1 -1
- data/example/square_tiling.rb +2 -2
- data/example/sudoku-set.rb +11 -12
- data/example/sudoku.rb +40 -45
- data/ext/extconf.rb +0 -0
- data/lib/gecoder/bindings.rb +42 -0
- data/lib/gecoder/bindings/bindings.rb +16 -0
- data/lib/gecoder/interface.rb +2 -1
- data/lib/gecoder/interface/branch.rb +16 -9
- data/lib/gecoder/interface/constraints.rb +410 -451
- data/lib/gecoder/interface/constraints/bool/boolean.rb +205 -213
- data/lib/gecoder/interface/constraints/bool/channel.rb +4 -5
- data/lib/gecoder/interface/constraints/bool/linear.rb +192 -21
- data/lib/gecoder/interface/constraints/bool_enum/channel.rb +43 -39
- data/lib/gecoder/interface/constraints/bool_enum/extensional.rb +43 -49
- data/lib/gecoder/interface/constraints/bool_enum/relation.rb +38 -71
- data/lib/gecoder/interface/constraints/bool_enum_constraints.rb +73 -22
- data/lib/gecoder/interface/constraints/bool_var_constraints.rb +140 -61
- data/lib/gecoder/interface/constraints/extensional_regexp.rb +4 -4
- data/lib/gecoder/interface/constraints/fixnum_enum/element.rb +63 -0
- data/lib/gecoder/interface/constraints/fixnum_enum/operation.rb +65 -0
- data/lib/gecoder/interface/constraints/fixnum_enum_constraints.rb +42 -0
- data/lib/gecoder/interface/constraints/int/arithmetic.rb +131 -130
- data/lib/gecoder/interface/constraints/int/channel.rb +21 -31
- data/lib/gecoder/interface/constraints/int/domain.rb +45 -42
- data/lib/gecoder/interface/constraints/int/linear.rb +85 -239
- data/lib/gecoder/interface/constraints/int/relation.rb +141 -0
- data/lib/gecoder/interface/constraints/int_enum/arithmetic.rb +55 -64
- data/lib/gecoder/interface/constraints/int_enum/channel.rb +35 -37
- data/lib/gecoder/interface/constraints/int_enum/count.rb +53 -78
- data/lib/gecoder/interface/constraints/int_enum/distinct.rb +36 -46
- data/lib/gecoder/interface/constraints/int_enum/element.rb +39 -57
- data/lib/gecoder/interface/constraints/int_enum/equality.rb +15 -19
- data/lib/gecoder/interface/constraints/int_enum/extensional.rb +65 -72
- data/lib/gecoder/interface/constraints/int_enum/sort.rb +42 -45
- data/lib/gecoder/interface/constraints/int_enum_constraints.rb +79 -22
- data/lib/gecoder/interface/constraints/int_var_constraints.rb +215 -44
- data/lib/gecoder/interface/constraints/reifiable_constraints.rb +14 -14
- data/lib/gecoder/interface/constraints/selected_set/select.rb +120 -0
- data/lib/gecoder/interface/constraints/selected_set_constraints.rb +75 -0
- data/lib/gecoder/interface/constraints/set/cardinality.rb +43 -53
- data/lib/gecoder/interface/constraints/set/channel.rb +26 -29
- data/lib/gecoder/interface/constraints/set/connection.rb +89 -152
- data/lib/gecoder/interface/constraints/set/domain.rb +112 -65
- data/lib/gecoder/interface/constraints/set/include.rb +36 -0
- data/lib/gecoder/interface/constraints/set/operation.rb +96 -110
- data/lib/gecoder/interface/constraints/set/relation.rb +114 -137
- data/lib/gecoder/interface/constraints/set_elements/relation.rb +116 -0
- data/lib/gecoder/interface/constraints/set_elements_constraints.rb +97 -0
- data/lib/gecoder/interface/constraints/set_enum/channel.rb +23 -27
- data/lib/gecoder/interface/constraints/set_enum/distinct.rb +18 -19
- data/lib/gecoder/interface/constraints/set_enum/operation.rb +62 -53
- data/lib/gecoder/interface/constraints/set_enum/select.rb +79 -0
- data/lib/gecoder/interface/constraints/set_enum_constraints.rb +73 -23
- data/lib/gecoder/interface/constraints/set_var_constraints.rb +222 -57
- data/lib/gecoder/interface/enum_matrix.rb +4 -4
- data/lib/gecoder/interface/enum_wrapper.rb +71 -22
- data/lib/gecoder/interface/model.rb +167 -12
- data/lib/gecoder/interface/model_sugar.rb +84 -0
- data/lib/gecoder/interface/search.rb +30 -18
- data/lib/gecoder/interface/variables.rb +103 -33
- data/lib/gecoder/version.rb +2 -2
- data/specs/bool_var.rb +19 -12
- data/specs/constraints/{boolean.rb → bool/boolean.rb} +103 -28
- data/specs/constraints/bool/boolean_properties.rb +51 -0
- data/specs/constraints/bool/linear.rb +213 -0
- data/specs/constraints/bool_enum/bool_enum_relation.rb +117 -0
- data/specs/constraints/bool_enum/channel.rb +102 -0
- data/specs/constraints/{extensional.rb → bool_enum/extensional.rb} +32 -101
- data/specs/constraints/constraint_helper.rb +149 -179
- data/specs/constraints/constraint_receivers.rb +103 -0
- data/specs/constraints/constraints.rb +6 -63
- data/specs/constraints/fixnum_enum/element.rb +58 -0
- data/specs/constraints/fixnum_enum/operation.rb +67 -0
- data/specs/constraints/int/arithmetic.rb +149 -0
- data/specs/constraints/int/channel.rb +101 -0
- data/specs/constraints/int/domain.rb +106 -0
- data/specs/constraints/int/linear.rb +183 -0
- data/specs/constraints/int/linear_properties.rb +97 -0
- data/specs/constraints/int/relation.rb +84 -0
- data/specs/constraints/int_enum/arithmetic.rb +72 -0
- data/specs/constraints/int_enum/channel.rb +57 -0
- data/specs/constraints/int_enum/count.rb +72 -0
- data/specs/constraints/int_enum/distinct.rb +80 -0
- data/specs/constraints/int_enum/element.rb +61 -0
- data/specs/constraints/int_enum/equality.rb +29 -0
- data/specs/constraints/int_enum/extensional.rb +224 -0
- data/specs/constraints/int_enum/sort.rb +167 -0
- data/specs/constraints/operands.rb +264 -0
- data/specs/constraints/property_helper.rb +443 -0
- data/specs/constraints/reification_sugar.rb +4 -5
- data/specs/constraints/selected_set/select.rb +56 -0
- data/specs/constraints/selected_set/select_properties.rb +157 -0
- data/specs/constraints/set/cardinality.rb +58 -0
- data/specs/constraints/set/cardinality_properties.rb +46 -0
- data/specs/constraints/set/channel.rb +77 -0
- data/specs/constraints/set/connection.rb +176 -0
- data/specs/constraints/set/domain.rb +197 -0
- data/specs/constraints/set/include.rb +36 -0
- data/specs/constraints/set/operation.rb +132 -0
- data/specs/constraints/set/relation.rb +117 -0
- data/specs/constraints/set_elements/relation.rb +84 -0
- data/specs/constraints/set_enum/channel.rb +80 -0
- data/specs/constraints/set_enum/distinct.rb +59 -0
- data/specs/constraints/set_enum/operation.rb +111 -0
- data/specs/constraints/set_enum/select.rb +73 -0
- data/specs/enum_wrapper.rb +53 -3
- data/specs/int_var.rb +44 -25
- data/specs/model.rb +58 -1
- data/specs/model_sugar.rb +30 -0
- data/specs/search.rb +24 -5
- data/specs/selected_set.rb +39 -0
- data/specs/set_elements.rb +34 -0
- data/specs/set_var.rb +22 -8
- data/specs/spec_helper.rb +206 -6
- data/tasks/distribution.rake +22 -7
- data/tasks/svn.rake +3 -1
- metadata +218 -134
- data/lib/gecoder/interface/constraints/set_enum/selection.rb +0 -217
- data/specs/constraints/arithmetic.rb +0 -351
- data/specs/constraints/bool_enum_relation.rb +0 -160
- data/specs/constraints/cardinality.rb +0 -157
- data/specs/constraints/channel.rb +0 -454
- data/specs/constraints/connection.rb +0 -369
- data/specs/constraints/count.rb +0 -146
- data/specs/constraints/distinct.rb +0 -164
- data/specs/constraints/element.rb +0 -108
- data/specs/constraints/equality.rb +0 -31
- data/specs/constraints/int_domain.rb +0 -70
- data/specs/constraints/int_relation.rb +0 -82
- data/specs/constraints/linear.rb +0 -340
- data/specs/constraints/selection.rb +0 -292
- data/specs/constraints/set_domain.rb +0 -185
- data/specs/constraints/set_operation.rb +0 -285
- data/specs/constraints/set_relation.rb +0 -197
- data/specs/constraints/sort.rb +0 -179
@@ -1,7 +1,7 @@
|
|
1
1
|
module Gecode
|
2
2
|
class Model
|
3
3
|
# Specifies which variables that should be branched on (given as an
|
4
|
-
# enum of
|
4
|
+
# enum of operands or as a single operand). One can optionally
|
5
5
|
# also select which of the variables that should be used first with
|
6
6
|
# the :variable option and which value in that variable's domain
|
7
7
|
# that should be used with the :value option. If nothing is
|
@@ -62,16 +62,23 @@ module Gecode
|
|
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?
|
65
|
+
if variables.respond_to?(:to_int_var) or
|
66
|
+
variables.respond_to?(:to_bool_var) or
|
67
|
+
variables.respond_to?(:to_set_var)
|
66
68
|
variables = wrap_enum [variables]
|
67
69
|
end
|
68
70
|
|
69
|
-
if variables.respond_to? :
|
70
|
-
|
71
|
-
|
71
|
+
if variables.respond_to? :to_int_enum
|
72
|
+
add_branch(variables.to_int_enum, options,
|
73
|
+
Constants::BRANCH_INT_VAR_CONSTANTS,
|
72
74
|
Constants::BRANCH_INT_VALUE_CONSTANTS)
|
73
|
-
elsif variables.respond_to? :
|
74
|
-
add_branch(variables, options,
|
75
|
+
elsif variables.respond_to? :to_bool_enum
|
76
|
+
add_branch(variables.to_bool_enum, options,
|
77
|
+
Constants::BRANCH_INT_VAR_CONSTANTS,
|
78
|
+
Constants::BRANCH_INT_VALUE_CONSTANTS)
|
79
|
+
elsif variables.respond_to? :to_set_enum
|
80
|
+
add_branch(variables.to_set_enum, options,
|
81
|
+
Constants::BRANCH_SET_VAR_CONSTANTS,
|
75
82
|
Constants::BRANCH_SET_VALUE_CONSTANTS)
|
76
83
|
else
|
77
84
|
raise TypeError, "Unknown type of variable enum #{variables.class}."
|
@@ -126,7 +133,7 @@ module Gecode
|
|
126
133
|
}
|
127
134
|
end
|
128
135
|
|
129
|
-
# Adds a branching selection for the specified
|
136
|
+
# Adds a branching selection for the specified variables with the specified
|
130
137
|
# options. The hashes are used to decode the options into Gecode's
|
131
138
|
# constants.
|
132
139
|
def add_branch(variables, options, branch_var_hash, branch_value_hash)
|
@@ -148,7 +155,7 @@ module Gecode
|
|
148
155
|
|
149
156
|
# Add the branching as a gecode interaction.
|
150
157
|
add_interaction do
|
151
|
-
Gecode::Raw.branch(active_space, variables.
|
158
|
+
Gecode::Raw.branch(active_space, variables.bind_array,
|
152
159
|
branch_var_hash[var_strat], branch_value_hash[val_strat])
|
153
160
|
end
|
154
161
|
end
|
@@ -4,499 +4,455 @@ module Gecode
|
|
4
4
|
class MissingConstraintError < StandardError
|
5
5
|
end
|
6
6
|
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@constraint_params.update(params_to_add)
|
33
|
-
end
|
7
|
+
# Describes an operand, something that a constraint can be placed
|
8
|
+
# on. Constraints are placed by calling #must or #must_not (the
|
9
|
+
# latter negates the constraint). This produces a
|
10
|
+
# ConstraintReceiver, which defines methods that places constraints
|
11
|
+
# on the operand.
|
12
|
+
#
|
13
|
+
# In general this produces something like the following.
|
14
|
+
#
|
15
|
+
# operand.must.constraint_method(params)
|
16
|
+
#
|
17
|
+
# See e.g. Gecode::Int::IntOperand for concrete examples.
|
18
|
+
#
|
19
|
+
# Classes that mix in Operand must define the methods #model
|
20
|
+
# and #construct_receiver. They should also define a method that converts
|
21
|
+
# the operand into a variable of the operand's type (e.g. int var
|
22
|
+
# operands should define a method #to_int_var that returns an
|
23
|
+
# instance of Gecode::IntVar that represents the operand). The
|
24
|
+
# latter method should be used by constraints to fetch variables
|
25
|
+
# needed when posting constraints. The presence of the method should
|
26
|
+
# also be used for type checking (rather than e.g. checking whether
|
27
|
+
# a parameter is of type IntOperand).
|
28
|
+
module Operand
|
29
|
+
# Specifies that a constraint must hold for the left hand side.
|
30
|
+
def must
|
31
|
+
construct_receiver :lhs => self, :negate => false
|
34
32
|
end
|
33
|
+
alias_method :must_be, :must
|
35
34
|
|
36
|
-
#
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
PROPAGATION_STRENGTHS = {
|
41
|
-
:default => Gecode::Raw::ICL_DEF,
|
42
|
-
:value => Gecode::Raw::ICL_VAL,
|
43
|
-
:bounds => Gecode::Raw::ICL_BND,
|
44
|
-
:domain => Gecode::Raw::ICL_DOM
|
45
|
-
}
|
46
|
-
|
47
|
-
# Maps the name used in options to the value used in Gecode for
|
48
|
-
# propagation kinds.
|
49
|
-
PROPAGATION_KINDS = {
|
50
|
-
:default => Gecode::Raw::PK_DEF,
|
51
|
-
:speed => Gecode::Raw::PK_SPEED,
|
52
|
-
:memory => Gecode::Raw::PK_MEMORY,
|
53
|
-
}
|
54
|
-
|
55
|
-
# Maps the names of the methods to the corresponding integer relation
|
56
|
-
# type in Gecode.
|
57
|
-
RELATION_TYPES = {
|
58
|
-
:== => Gecode::Raw::IRT_EQ,
|
59
|
-
:<= => Gecode::Raw::IRT_LQ,
|
60
|
-
:< => Gecode::Raw::IRT_LE,
|
61
|
-
:>= => Gecode::Raw::IRT_GQ,
|
62
|
-
:> => Gecode::Raw::IRT_GR
|
63
|
-
}
|
64
|
-
# The same as above, but negated.
|
65
|
-
NEGATED_RELATION_TYPES = {
|
66
|
-
:== => Gecode::Raw::IRT_NQ,
|
67
|
-
:<= => Gecode::Raw::IRT_GR,
|
68
|
-
:< => Gecode::Raw::IRT_GQ,
|
69
|
-
:>= => Gecode::Raw::IRT_LE,
|
70
|
-
:> => Gecode::Raw::IRT_LQ
|
71
|
-
}
|
72
|
-
|
73
|
-
# Maps the names of the methods to the corresponding set relation type in
|
74
|
-
# Gecode.
|
75
|
-
SET_RELATION_TYPES = {
|
76
|
-
:== => Gecode::Raw::SRT_EQ,
|
77
|
-
:superset => Gecode::Raw::SRT_SUP,
|
78
|
-
:subset => Gecode::Raw::SRT_SUB,
|
79
|
-
:disjoint => Gecode::Raw::SRT_DISJ,
|
80
|
-
:complement => Gecode::Raw::SRT_CMPL
|
81
|
-
}
|
82
|
-
# The same as above, but negated.
|
83
|
-
NEGATED_SET_RELATION_TYPES = {
|
84
|
-
:== => Gecode::Raw::SRT_NQ
|
85
|
-
}
|
86
|
-
# Maps the names of the methods to the corresponding set operation type in
|
87
|
-
# Gecode.
|
88
|
-
SET_OPERATION_TYPES = {
|
89
|
-
:union => Gecode::Raw::SOT_UNION,
|
90
|
-
:disjoint_union => Gecode::Raw::SOT_DUNION,
|
91
|
-
:intersection => Gecode::Raw::SOT_INTER,
|
92
|
-
:minus => Gecode::Raw::SOT_MINUS
|
93
|
-
}
|
94
|
-
|
95
|
-
# Various method aliases for comparison methods. Maps the original
|
96
|
-
# (symbol) name to an array of aliases.
|
97
|
-
COMPARISON_ALIASES = {
|
98
|
-
:== => [:equal, :equal_to],
|
99
|
-
:> => [:greater, :greater_than],
|
100
|
-
:>= => [:greater_or_equal, :greater_than_or_equal_to],
|
101
|
-
:< => [:less, :less_than],
|
102
|
-
:<= => [:less_or_equal, :less_than_or_equal_to]
|
103
|
-
}
|
104
|
-
SET_ALIASES = {
|
105
|
-
:== => [:equal, :equal_to],
|
106
|
-
:superset => [:superset_of],
|
107
|
-
:subset => [:subset_of],
|
108
|
-
:disjoint => [:disjoint_with],
|
109
|
-
:complement => [:complement_of]
|
110
|
-
}
|
111
|
-
|
112
|
-
module_function
|
113
|
-
|
114
|
-
# Decodes the common options to constraints: strength, kind and
|
115
|
-
# reification. Returns a hash with up to three values. :strength is the
|
116
|
-
# strength that should be used for the constraint, :kind is the
|
117
|
-
# propagation kind that should be used, and :reif is the (bound) boolean
|
118
|
-
# variable that should be used for reification. The decoded options are
|
119
|
-
# removed from the hash (so in general the hash will be consumed in the
|
120
|
-
# process).
|
121
|
-
#
|
122
|
-
# Raises ArgumentError if an unrecognized option is found in the specified
|
123
|
-
# hash. Or if an unrecognized strength is given. Raises TypeError if the
|
124
|
-
# reification variable is not a boolean variable.
|
125
|
-
def decode_options(options)
|
126
|
-
# Propagation strength.
|
127
|
-
strength = options.delete(:strength) || :default
|
128
|
-
unless PROPAGATION_STRENGTHS.include? strength
|
129
|
-
raise ArgumentError, "Unrecognized propagation strength #{strength}."
|
130
|
-
end
|
131
|
-
|
132
|
-
# Propagation kind.
|
133
|
-
kind = options.delete(:kind) || :default
|
134
|
-
unless PROPAGATION_KINDS.include? kind
|
135
|
-
raise ArgumentError, "Unrecognized propagation kind #{kind}."
|
136
|
-
end
|
137
|
-
|
138
|
-
# Reification.
|
139
|
-
reif_var = options.delete(:reify)
|
140
|
-
unless reif_var.nil? or reif_var.kind_of? FreeBoolVar
|
141
|
-
raise TypeError, 'Only boolean variables may be used for reification.'
|
142
|
-
end
|
143
|
-
|
144
|
-
# Check for unrecognized options.
|
145
|
-
unless options.empty?
|
146
|
-
raise ArgumentError, 'Unrecognized constraint option: ' +
|
147
|
-
options.keys.first.to_s
|
148
|
-
end
|
149
|
-
return {
|
150
|
-
:strength => PROPAGATION_STRENGTHS[strength],
|
151
|
-
:kind => PROPAGATION_KINDS[kind],
|
152
|
-
:reif => reif_var
|
153
|
-
}
|
154
|
-
end
|
155
|
-
|
156
|
-
# Converts the different ways to specify constant sets in the interface
|
157
|
-
# to the form that the set should be represented in Gecode (possibly
|
158
|
-
# multiple paramters. The different forms accepted are:
|
159
|
-
# * Single instance of Fixnum (singleton set).
|
160
|
-
# * Range (set containing all numbers in range), treated differently from
|
161
|
-
# other enumerations.
|
162
|
-
# * Enumeration of integers (set contaning all numbers in set).
|
163
|
-
def constant_set_to_params(constant_set)
|
164
|
-
unless constant_set?(constant_set)
|
165
|
-
raise TypeError, "Expected a constant set, got: #{constant_set}."
|
166
|
-
end
|
167
|
-
|
168
|
-
if constant_set.kind_of? Range
|
169
|
-
return constant_set.first, constant_set.last
|
170
|
-
elsif constant_set.kind_of? Fixnum
|
171
|
-
return constant_set
|
172
|
-
else
|
173
|
-
constant_set = constant_set.to_a
|
174
|
-
return Gecode::Raw::IntSet.new(constant_set, constant_set.size)
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
# Converts the different ways to specify constant sets in the interface
|
179
|
-
# to an instance of Gecode::Raw::IntSet. The different forms accepted are:
|
180
|
-
# * Single instance of Fixnum (singleton set).
|
181
|
-
# * Range (set containing all numbers in range), treated differently from
|
182
|
-
# other enumerations.
|
183
|
-
# * Enumeration of integers (set contaning all numbers in set).
|
184
|
-
def constant_set_to_int_set(constant_set)
|
185
|
-
unless constant_set?(constant_set)
|
186
|
-
raise TypeError, "Expected a constant set, got: #{constant_set}."
|
187
|
-
end
|
188
|
-
|
189
|
-
if constant_set.kind_of? Range
|
190
|
-
return Gecode::Raw::IntSet.new(constant_set.first, constant_set.last)
|
191
|
-
elsif constant_set.kind_of? Fixnum
|
192
|
-
return Gecode::Raw::IntSet.new([constant_set], 1)
|
193
|
-
else
|
194
|
-
constant_set = constant_set.to_a
|
195
|
-
return Gecode::Raw::IntSet.new(constant_set, constant_set.size)
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
# Checks whether the specified expression is regarded as a constant set.
|
200
|
-
# Returns true if it is, false otherwise.
|
201
|
-
def constant_set?(expression)
|
202
|
-
return (
|
203
|
-
expression.kind_of?(Range) && # It's a range.
|
204
|
-
expression.first.kind_of?(Fixnum) &&
|
205
|
-
expression.last.kind_of?(Fixnum)) ||
|
206
|
-
expression.kind_of?(Fixnum) || # It's a single fixnum.
|
207
|
-
(expression.kind_of?(Enumerable) && # It's an enum of fixnums.
|
208
|
-
expression.all?{ |e| e.kind_of? Fixnum })
|
209
|
-
end
|
210
|
-
|
211
|
-
# Extracts an array of the values selected for the standard propagation
|
212
|
-
# options (propagation strength and propagation kind) from the hash of
|
213
|
-
# parameters given. The options are returned in the order that they are
|
214
|
-
# given when posting constraints to Gecode.
|
215
|
-
def extract_propagation_options(params)
|
216
|
-
params.values_at(:strength, :kind)
|
217
|
-
end
|
35
|
+
# Specifies that the negation of a constraint must hold for the left hand
|
36
|
+
# side.
|
37
|
+
def must_not
|
38
|
+
construct_receiver :lhs => self, :negate => true
|
218
39
|
end
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
# Checks that the specified enumeration is an enumeration containing
|
225
|
-
# one or more tuples of the specified size. It also allows the caller
|
226
|
-
# to define additional tests by providing a block, which is given each
|
227
|
-
# tuple. If a test fails then an appropriate error is raised.
|
228
|
-
def perform_tuple_checks(tuples, expected_size, &additional_test)
|
229
|
-
unless tuples.respond_to?(:each)
|
230
|
-
raise TypeError, 'Expected an enumeration with tuples, got ' +
|
231
|
-
"#{tuples.class}."
|
232
|
-
end
|
233
|
-
|
234
|
-
if tuples.empty?
|
235
|
-
raise ArgumentError, 'One or more tuples must be specified.'
|
236
|
-
end
|
237
|
-
|
238
|
-
tuples.each do |tuple|
|
239
|
-
unless tuple.respond_to?(:each)
|
240
|
-
raise TypeError, 'Expected an enumeration containing enumeraions, ' +
|
241
|
-
"got #{tuple.class}."
|
242
|
-
end
|
243
|
-
|
244
|
-
unless tuple.size == expected_size
|
245
|
-
raise ArgumentError, 'All tuples must be of the same size as the ' +
|
246
|
-
'number of variables in the array.'
|
247
|
-
end
|
248
|
-
|
249
|
-
yield tuple
|
250
|
-
end
|
251
|
-
end
|
40
|
+
alias_method :must_not_be, :must_not
|
41
|
+
|
42
|
+
# Fetches the model that the operand belongs to.
|
43
|
+
def model
|
44
|
+
raise NotImplementedError, 'Abstract method has not been implemented.'
|
252
45
|
end
|
253
46
|
|
254
|
-
|
255
|
-
|
256
|
-
#
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
47
|
+
private
|
48
|
+
|
49
|
+
# Constructs the appropriate constraint receiver given the
|
50
|
+
# specified parameters.
|
51
|
+
def construct_receiver(params)
|
52
|
+
raise NotImplementedError, 'Abstract method has not been implemented.'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Describes a constraint receiver, something that receives and
|
57
|
+
# places constraints on various Operand. Constraint receivers
|
58
|
+
# are created by calling #must or #must_not (the latter negates
|
59
|
+
# the constraint) on something that mixes in Operand.
|
60
|
+
#
|
61
|
+
# A constraint is placed on an Operand +operand+ as follows:
|
62
|
+
#
|
63
|
+
# operand.must.constraint_method(params)
|
64
|
+
#
|
65
|
+
# The constraint receiver is created by the call to #must and the
|
66
|
+
# constraint is then placed by the call to #constraint_method.
|
67
|
+
# See e.g. Gecode::Int::IntConstraintReceiver for
|
68
|
+
# concrete examples.
|
69
|
+
#
|
70
|
+
# The following options can be specified in a hash with symbols as
|
71
|
+
# keys when placing a constraint:
|
72
|
+
#
|
73
|
+
# [:strength] The propagation strength suggests how much effort the
|
74
|
+
# solver should put into trying to prune the domains of
|
75
|
+
# variables using the constraint.
|
76
|
+
#
|
77
|
+
# The allowed values are:
|
78
|
+
# [:value] Value consistency (naive).
|
79
|
+
# [:bounds] Bounds consistency. The bounds of the operand
|
80
|
+
# will always be constrained as much as possible
|
81
|
+
# (but pruning may not be done inside the
|
82
|
+
# bounds, even though it may be possible).
|
83
|
+
# [:domain] Domain consistency. All values that can be pruned
|
84
|
+
# away, given the current amount of information,
|
85
|
+
# are pruned away.
|
86
|
+
# [:default] Uses the default consistency of the constraint.
|
87
|
+
#
|
88
|
+
# The strength generally progresses as
|
89
|
+
# :value < :bounds < :domain (:value being the weakest,
|
90
|
+
# :domain being the strongest). A higher strength can
|
91
|
+
# reduce the search space quicker, but at the cost of
|
92
|
+
# making each propagation more costly.
|
93
|
+
#
|
94
|
+
# [:kind] The propagation kind option suggests the implementation
|
95
|
+
# that should be preferred if there are multiple
|
96
|
+
# implementations of a constraint.
|
97
|
+
#
|
98
|
+
# The different kinds are:
|
99
|
+
# [:speed] Prefer speed over memory consumption.
|
100
|
+
# [:memory] Prefer low memory consumption over speed.
|
101
|
+
# [:default] Uses the constraint's default propagation kind.
|
102
|
+
#
|
103
|
+
# [:reify] Reification is used to link a constraint to a boolean
|
104
|
+
# operand in such a way that the variable is true if and
|
105
|
+
# only if the constraint is satisfied. The propagation
|
106
|
+
# goes both ways, so if the variable is constrained to be
|
107
|
+
# false then the constraint is not allowed to be
|
108
|
+
# satisfied.
|
109
|
+
#
|
110
|
+
# Reification can be thought of as a last resort glue which
|
111
|
+
# can be used to combine constraints so that e.g. exactly
|
112
|
+
# 3 out of 17 constraints must be satisfied.
|
113
|
+
#
|
114
|
+
# Not all constraints accept all options. Constraints that have sets
|
115
|
+
# as operands (e.g. SetConstraintReceiver and
|
116
|
+
# SetEnumConstraintReceiver) do not accept the :strength and :kind
|
117
|
+
# options, all other do. Some constraints do not accept the :reify
|
118
|
+
# option.
|
119
|
+
#
|
120
|
+
# See e.g. Gecode::Int::IntConstraintReceiver for
|
121
|
+
# concrete examples of options being specified.
|
122
|
+
class ConstraintReceiver
|
123
|
+
# Constructs a new expression with the specified parameters. The
|
124
|
+
# parameters should at least contain the keys :lhs, and :negate.
|
125
|
+
#
|
126
|
+
# Raises ArgumentError if any of those keys are missing or if :lhs
|
127
|
+
# is not an operand.
|
128
|
+
def initialize(model, params)
|
129
|
+
unless params.has_key?(:lhs) and params.has_key?(:negate)
|
130
|
+
raise ArgumentError, 'Expression requires at least :lhs, ' +
|
131
|
+
"and :negate as parameter keys, got #{params.keys.join(', ')}."
|
270
132
|
end
|
271
|
-
|
272
|
-
|
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
|
133
|
+
unless params[:lhs].kind_of? Operand
|
134
|
+
raise ArgumentError, 'Expected :lhs to be an operand, received ' +
|
135
|
+
"#{params[:lhs].class}."
|
309
136
|
end
|
310
137
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
138
|
+
@model = model
|
139
|
+
@params = params
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
# Provides commutativity for the constraint with the specified
|
145
|
+
# method name. If the method with the specified method name is
|
146
|
+
# called with something that, when given to the block, evaluates
|
147
|
+
# to true, then the constraint will be called on the right hand
|
148
|
+
# side with the left hand side as argument.
|
149
|
+
#
|
150
|
+
# The original constraint method is assumed to take two arguments:
|
151
|
+
# a right hand side and a hash of options.
|
152
|
+
def self.provide_commutativity(constraint_name, &block)
|
153
|
+
unique_id = constraint_name.to_sym.to_i
|
154
|
+
pre_alias_method_name = 'pre_commutivity_' << unique_id.to_s
|
155
|
+
if method_defined? constraint_name
|
156
|
+
alias_method pre_alias_method_name, constraint_name
|
320
157
|
end
|
321
158
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
if
|
326
|
-
|
327
|
-
|
159
|
+
module_eval <<-end_code
|
160
|
+
@@commutivity_check_#{unique_id} = block
|
161
|
+
def #{constraint_name}(rhs, options = {})
|
162
|
+
if @@commutivity_check_#{unique_id}.call(rhs, options)
|
163
|
+
if @params[:negate]
|
164
|
+
rhs.must_not.method(:#{constraint_name}).call(
|
165
|
+
@params[:lhs], options)
|
166
|
+
else
|
167
|
+
rhs.must.method(:#{constraint_name}).call(
|
168
|
+
@params[:lhs], options)
|
169
|
+
end
|
170
|
+
else
|
171
|
+
if self.class.method_defined? :#{pre_alias_method_name}
|
172
|
+
#{pre_alias_method_name}(rhs, options)
|
173
|
+
else
|
174
|
+
raise TypeError, \"Unexpected argument type \#{rhs.class}.\"
|
328
175
|
end
|
329
176
|
end
|
330
177
|
end
|
331
|
-
|
178
|
+
end_code
|
332
179
|
end
|
333
180
|
|
334
|
-
#
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
# which has an unconstricted domain. The block given should take three
|
341
|
-
# parameters. The first is the variable that should be the left hand side.
|
342
|
-
# The second is the hash of parameters. The third is a boolean, it it's
|
343
|
-
# true then the block should try to constrain the first variable's domain
|
344
|
-
# as much as possible.
|
345
|
-
def initialize(expression_class, variable_class, new_var_proc, model,
|
346
|
-
params, &block)
|
347
|
-
super(model, params)
|
348
|
-
@expression_class = expression_class
|
349
|
-
@variable_class = variable_class
|
350
|
-
@new_var_proc = new_var_proc
|
351
|
-
@constrain_equal_proc = block
|
352
|
-
end
|
353
|
-
|
354
|
-
# Delegate to an instance of the expression class when we get something
|
355
|
-
# that we can't handle.
|
356
|
-
def method_missing(name, *args)
|
357
|
-
if @expression_class.instance_methods.include? name.to_s
|
358
|
-
options = {}
|
359
|
-
if args.size >= 2 and args[1].kind_of? Hash
|
360
|
-
options = args[1]
|
181
|
+
# Creates aliases for any defined comparison methods.
|
182
|
+
def self.alias_comparison_methods
|
183
|
+
Gecode::Util::COMPARISON_ALIASES.each_pair do |orig, aliases|
|
184
|
+
if instance_methods.include?(orig.to_s)
|
185
|
+
aliases.each do |name|
|
186
|
+
alias_method(name, orig)
|
361
187
|
end
|
362
|
-
|
363
|
-
# Link a variable to the composite constraint.
|
364
|
-
@params.update Gecode::Constraints::Util.decode_options(options.clone)
|
365
|
-
variable = @new_var_proc.call
|
366
|
-
@model.add_interaction do
|
367
|
-
@constrain_equal_proc.call(variable, @params, true)
|
368
|
-
end
|
369
|
-
|
370
|
-
# Perform the operation on the linked variable.
|
371
|
-
int_var_params = @params.clone.update(:lhs => variable)
|
372
|
-
@expression_class.new(@model, int_var_params).send(name, *args)
|
373
|
-
else
|
374
|
-
super
|
375
188
|
end
|
376
189
|
end
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
190
|
+
end
|
191
|
+
|
192
|
+
# Creates aliases for any defined set methods.
|
193
|
+
def self.alias_set_methods
|
194
|
+
Gecode::Util::SET_ALIASES.each_pair do |orig, aliases|
|
195
|
+
if instance_methods.include?(orig.to_s)
|
196
|
+
aliases.each do |name|
|
197
|
+
alias_method(name, orig)
|
385
198
|
end
|
386
|
-
else
|
387
|
-
method_missing(:==, expression, options)
|
388
199
|
end
|
389
200
|
end
|
390
|
-
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Base class for all constraints.
|
205
|
+
class Constraint #:nodoc:
|
206
|
+
# Creates a constraint with the specified parameters, bound to the
|
207
|
+
# specified model.
|
208
|
+
def initialize(model, params)
|
209
|
+
@model = model
|
210
|
+
@params = params.clone
|
391
211
|
end
|
392
212
|
|
393
|
-
#
|
394
|
-
#
|
395
|
-
|
396
|
-
|
397
|
-
# enum.with_offsets(0..n).must_be.distinct
|
398
|
-
#
|
399
|
-
# The call of with_offsets initiates the constraint as a stub, even though
|
400
|
-
# must has not yet been called.
|
401
|
-
class ExpressionStub #:nodoc:
|
402
|
-
# Constructs a new expression with the specified parameters.
|
403
|
-
def initialize(model, params)
|
404
|
-
@model = model
|
405
|
-
@params = params
|
406
|
-
end
|
213
|
+
# Posts the constraint, adding it to the model. This is an abstract
|
214
|
+
# method and should be overridden by all sub-classes.
|
215
|
+
def post
|
216
|
+
raise NotImplementedError, 'Abstract method has not been implemented.'
|
407
217
|
end
|
408
218
|
|
409
|
-
|
410
|
-
# just sends models and parameters through a supplied block to construct the
|
411
|
-
# resulting expression.
|
412
|
-
class SimpleExpressionStub < ExpressionStub #:nodoc:
|
413
|
-
include Gecode::Constraints::LeftHandSideMethods
|
219
|
+
private
|
414
220
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
221
|
+
# Gives an array of the values selected for the standard propagation
|
222
|
+
# options (propagation strength and propagation kind) in the order that
|
223
|
+
# they are given when posting constraints to Gecode.
|
224
|
+
def propagation_options
|
225
|
+
Gecode::Util::extract_propagation_options(@params)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# A constraint that can be specified by providing a block containing the
|
230
|
+
# post method.
|
231
|
+
class BlockConstraint < Constraint #:nodoc:
|
232
|
+
def initialize(model, params, &block)
|
233
|
+
super(model, params)
|
234
|
+
@proc = block
|
235
|
+
end
|
236
|
+
|
237
|
+
def post
|
238
|
+
@proc.call
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# A module that provides some utility-methods for constraints.
|
243
|
+
module Util #:nodoc:
|
244
|
+
# Maps the name used in options to the value used in Gecode for
|
245
|
+
# propagation strengths.
|
246
|
+
PROPAGATION_STRENGTHS = {
|
247
|
+
:default => Gecode::Raw::ICL_DEF,
|
248
|
+
:value => Gecode::Raw::ICL_VAL,
|
249
|
+
:bounds => Gecode::Raw::ICL_BND,
|
250
|
+
:domain => Gecode::Raw::ICL_DOM
|
251
|
+
}
|
252
|
+
|
253
|
+
# Maps the name used in options to the value used in Gecode for
|
254
|
+
# propagation kinds.
|
255
|
+
PROPAGATION_KINDS = {
|
256
|
+
:default => Gecode::Raw::PK_DEF,
|
257
|
+
:speed => Gecode::Raw::PK_SPEED,
|
258
|
+
:memory => Gecode::Raw::PK_MEMORY,
|
259
|
+
}
|
260
|
+
|
261
|
+
# Maps the names of the methods to the corresponding integer relation
|
262
|
+
# type in Gecode.
|
263
|
+
RELATION_TYPES = {
|
264
|
+
:== => Gecode::Raw::IRT_EQ,
|
265
|
+
:<= => Gecode::Raw::IRT_LQ,
|
266
|
+
:< => Gecode::Raw::IRT_LE,
|
267
|
+
:>= => Gecode::Raw::IRT_GQ,
|
268
|
+
:> => Gecode::Raw::IRT_GR
|
269
|
+
}
|
270
|
+
# The same as above, but negated.
|
271
|
+
NEGATED_RELATION_TYPES = {
|
272
|
+
:== => Gecode::Raw::IRT_NQ,
|
273
|
+
:<= => Gecode::Raw::IRT_GR,
|
274
|
+
:< => Gecode::Raw::IRT_GQ,
|
275
|
+
:>= => Gecode::Raw::IRT_LE,
|
276
|
+
:> => Gecode::Raw::IRT_LQ
|
277
|
+
}
|
278
|
+
|
279
|
+
# Maps the names of the methods to the corresponding set relation type in
|
280
|
+
# Gecode.
|
281
|
+
SET_RELATION_TYPES = {
|
282
|
+
:== => Gecode::Raw::SRT_EQ,
|
283
|
+
:superset => Gecode::Raw::SRT_SUP,
|
284
|
+
:subset => Gecode::Raw::SRT_SUB,
|
285
|
+
:disjoint => Gecode::Raw::SRT_DISJ,
|
286
|
+
:complement => Gecode::Raw::SRT_CMPL
|
287
|
+
}
|
288
|
+
# The same as above, but negated.
|
289
|
+
NEGATED_SET_RELATION_TYPES = {
|
290
|
+
:== => Gecode::Raw::SRT_NQ
|
291
|
+
}
|
292
|
+
# Maps the names of the methods to the corresponding set operation type in
|
293
|
+
# Gecode.
|
294
|
+
SET_OPERATION_TYPES = {
|
295
|
+
:union => Gecode::Raw::SOT_UNION,
|
296
|
+
:disjoint_union => Gecode::Raw::SOT_DUNION,
|
297
|
+
:intersection => Gecode::Raw::SOT_INTER,
|
298
|
+
:minus => Gecode::Raw::SOT_MINUS
|
299
|
+
}
|
300
|
+
|
301
|
+
# Various method aliases for comparison methods. Maps the original
|
302
|
+
# (symbol) name to an array of aliases.
|
303
|
+
COMPARISON_ALIASES = {
|
304
|
+
:== => [:equal, :equal_to],
|
305
|
+
:> => [:greater, :greater_than],
|
306
|
+
:>= => [:greater_or_equal, :greater_than_or_equal_to],
|
307
|
+
:< => [:less, :less_than],
|
308
|
+
:<= => [:less_or_equal, :less_than_or_equal_to]
|
309
|
+
}
|
310
|
+
SET_ALIASES = {
|
311
|
+
:== => [:equal, :equal_to],
|
312
|
+
:superset => [:superset_of],
|
313
|
+
:subset => [:subset_of],
|
314
|
+
:disjoint => [:disjoint_with],
|
315
|
+
:complement => [:complement_of]
|
316
|
+
}
|
317
|
+
|
318
|
+
module_function
|
319
|
+
|
320
|
+
# Decodes the common options to constraints: strength, kind and
|
321
|
+
# reification. Returns a hash with up to three values. :strength is the
|
322
|
+
# strength that should be used for the constraint, :kind is the
|
323
|
+
# propagation kind that should be used, and :reif is the (bound) boolean
|
324
|
+
# operand that should be used for reification. The decoded options are
|
325
|
+
# removed from the hash (so in general the hash will be consumed in the
|
326
|
+
# process).
|
327
|
+
#
|
328
|
+
# Raises ArgumentError if an unrecognized option is found in the specified
|
329
|
+
# hash. Or if an unrecognized strength is given. Raises TypeError if the
|
330
|
+
# reification operand is not a boolean operand.
|
331
|
+
def decode_options(options)
|
332
|
+
# Propagation strength.
|
333
|
+
strength = options.delete(:strength) || :default
|
334
|
+
unless PROPAGATION_STRENGTHS.include? strength
|
335
|
+
raise ArgumentError, "Unrecognized propagation strength #{strength}."
|
422
336
|
end
|
423
337
|
|
424
|
-
|
338
|
+
# Propagation kind.
|
339
|
+
kind = options.delete(:kind) || :default
|
340
|
+
unless PROPAGATION_KINDS.include? kind
|
341
|
+
raise ArgumentError, "Unrecognized propagation kind #{kind}."
|
342
|
+
end
|
343
|
+
|
344
|
+
# Reification.
|
345
|
+
reif_var = options.delete(:reify)
|
346
|
+
unless reif_var.nil? or reif_var.respond_to? :to_bool_var
|
347
|
+
raise TypeError, 'Only boolean operands may be used for reification.'
|
348
|
+
end
|
425
349
|
|
426
|
-
#
|
427
|
-
|
428
|
-
|
429
|
-
|
350
|
+
# Check for unrecognized options.
|
351
|
+
unless options.empty?
|
352
|
+
raise ArgumentError, 'Unrecognized constraint option: ' +
|
353
|
+
options.keys.first.to_s
|
430
354
|
end
|
355
|
+
return {
|
356
|
+
:strength => PROPAGATION_STRENGTHS[strength],
|
357
|
+
:kind => PROPAGATION_KINDS[kind],
|
358
|
+
:reif => reif_var
|
359
|
+
}
|
431
360
|
end
|
432
361
|
|
433
|
-
#
|
434
|
-
# that
|
435
|
-
#
|
436
|
-
#
|
437
|
-
#
|
438
|
-
#
|
439
|
-
#
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
class CompositeStub < Gecode::Constraints::ExpressionStub #:nodoc:
|
444
|
-
include Gecode::Constraints::LeftHandSideMethods
|
445
|
-
|
446
|
-
# The composite expression class should be the class that the stub uses
|
447
|
-
# when creating its expressions.
|
448
|
-
def initialize(composite_expression_class, model, params)
|
449
|
-
super(model, params)
|
450
|
-
@composite_class = composite_expression_class
|
362
|
+
# Converts the different ways to specify constant sets in the interface
|
363
|
+
# to the form that the set should be represented in Gecode (possibly
|
364
|
+
# multiple paramters. The different forms accepted are:
|
365
|
+
# * Single instance of Fixnum (singleton set).
|
366
|
+
# * Range (set containing all numbers in range), treated differently from
|
367
|
+
# other enumerations.
|
368
|
+
# * Enumeration of integers (set contaning all numbers in set).
|
369
|
+
def constant_set_to_params(constant_set)
|
370
|
+
unless constant_set?(constant_set)
|
371
|
+
raise TypeError, "Expected a constant set, got: #{constant_set}."
|
451
372
|
end
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
373
|
+
|
374
|
+
if constant_set.kind_of? Range
|
375
|
+
return constant_set.first, constant_set.last
|
376
|
+
elsif constant_set.kind_of? Fixnum
|
377
|
+
return constant_set
|
378
|
+
else
|
379
|
+
constant_set = constant_set.to_a
|
380
|
+
return Gecode::Raw::IntSet.new(constant_set, constant_set.size)
|
460
381
|
end
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
382
|
+
end
|
383
|
+
|
384
|
+
# Converts the different ways to specify constant sets in the interface
|
385
|
+
# to an instance of Gecode::Raw::IntSet. The different forms accepted are:
|
386
|
+
# * Single instance of Fixnum (singleton set).
|
387
|
+
# * Range (set containing all numbers in range), treated differently from
|
388
|
+
# other enumerations.
|
389
|
+
# * Enumeration of integers (set contaning all numbers in set).
|
390
|
+
def constant_set_to_int_set(constant_set)
|
391
|
+
unless constant_set?(constant_set)
|
392
|
+
raise TypeError, "Expected a constant set, got: #{constant_set}."
|
468
393
|
end
|
469
394
|
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
395
|
+
if constant_set.kind_of? Range
|
396
|
+
return Gecode::Raw::IntSet.new(constant_set.first, constant_set.last)
|
397
|
+
elsif constant_set.kind_of? Fixnum
|
398
|
+
return Gecode::Raw::IntSet.new([constant_set], 1)
|
399
|
+
else
|
400
|
+
constant_set = constant_set.to_a
|
401
|
+
return Gecode::Raw::IntSet.new(constant_set, constant_set.size)
|
402
|
+
end
|
476
403
|
end
|
477
404
|
|
478
|
-
#
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
405
|
+
# Checks whether the specified expression is regarded as a constant set.
|
406
|
+
# Returns true if it is, false otherwise.
|
407
|
+
def constant_set?(expression)
|
408
|
+
return (
|
409
|
+
expression.kind_of?(Range) && # It's a range.
|
410
|
+
expression.first.kind_of?(Fixnum) &&
|
411
|
+
expression.last.kind_of?(Fixnum)) ||
|
412
|
+
expression.kind_of?(Fixnum) || # It's a single fixnum.
|
413
|
+
(expression.kind_of?(Enumerable) && # It's an enum of fixnums.
|
414
|
+
expression.all?{ |e| e.kind_of? Fixnum })
|
415
|
+
end
|
416
|
+
|
417
|
+
# Extracts an array of the values selected for the standard propagation
|
418
|
+
# options (propagation strength and propagation kind) from the hash of
|
419
|
+
# parameters given. The options are returned in the order that they are
|
420
|
+
# given when posting constraints to Gecode.
|
421
|
+
def extract_propagation_options(params)
|
422
|
+
params.values_at(:strength, :kind)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
# A module that contains utility-methods for extensional constraints.
|
427
|
+
module Util::Extensional #:nodoc:
|
428
|
+
module_function
|
429
|
+
|
430
|
+
# Checks that the specified enumeration is an enumeration containing
|
431
|
+
# one or more tuples of the specified size. It also allows the caller
|
432
|
+
# to define additional tests by providing a block, which is given each
|
433
|
+
# tuple. If a test fails then an appropriate error is raised.
|
434
|
+
def perform_tuple_checks(tuples, expected_size, &additional_test)
|
435
|
+
unless tuples.respond_to?(:each)
|
436
|
+
raise TypeError, 'Expected an enumeration with tuples, got ' +
|
437
|
+
"#{tuples.class}."
|
485
438
|
end
|
486
439
|
|
487
|
-
|
488
|
-
|
489
|
-
def post
|
490
|
-
raise NoMethodError, 'Abstract method has not been implemented.'
|
440
|
+
if tuples.empty?
|
441
|
+
raise ArgumentError, 'One or more tuples must be specified.'
|
491
442
|
end
|
492
443
|
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
444
|
+
tuples.each do |tuple|
|
445
|
+
unless tuple.respond_to?(:each)
|
446
|
+
raise TypeError, 'Expected an enumeration containing enumeraions, ' +
|
447
|
+
"got #{tuple.class}."
|
448
|
+
end
|
449
|
+
|
450
|
+
unless tuple.size == expected_size
|
451
|
+
raise ArgumentError, 'All tuples must be of the same size as the ' +
|
452
|
+
'number of operands in the array.'
|
453
|
+
end
|
454
|
+
|
455
|
+
yield tuple
|
500
456
|
end
|
501
457
|
end
|
502
458
|
end
|
@@ -509,4 +465,7 @@ require 'gecoder/interface/constraints/bool_var_constraints'
|
|
509
465
|
require 'gecoder/interface/constraints/bool_enum_constraints'
|
510
466
|
require 'gecoder/interface/constraints/set_var_constraints'
|
511
467
|
require 'gecoder/interface/constraints/set_enum_constraints'
|
468
|
+
require 'gecoder/interface/constraints/selected_set_constraints'
|
469
|
+
require 'gecoder/interface/constraints/set_elements_constraints'
|
470
|
+
require 'gecoder/interface/constraints/fixnum_enum_constraints'
|
512
471
|
require 'gecoder/interface/constraints/extensional_regexp'
|