gecoder 0.5.0 → 0.6.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 +16 -3
- data/example/magic_sequence.rb +1 -1
- data/example/queens.rb +1 -1
- data/example/send_more_money.rb +1 -1
- data/example/sudoku.rb +1 -1
- data/ext/missing.cpp +18 -4
- data/ext/missing.h +8 -0
- data/lib/gecoder/bindings.rb +30 -3
- data/lib/gecoder/bindings/bindings.rb +22 -0
- data/lib/gecoder/interface/binding_changes.rb +81 -107
- data/lib/gecoder/interface/branch.rb +65 -14
- data/lib/gecoder/interface/constraints.rb +1 -0
- data/lib/gecoder/interface/constraints/bool_enum/boolean.rb +16 -12
- data/lib/gecoder/interface/constraints/int/arithmetic.rb +7 -3
- data/lib/gecoder/interface/constraints/int/linear.rb +19 -16
- data/lib/gecoder/interface/constraints/int_enum/arithmetic.rb +8 -4
- data/lib/gecoder/interface/constraints/int_enum/channel.rb +14 -6
- data/lib/gecoder/interface/constraints/int_enum/element.rb +7 -5
- data/lib/gecoder/interface/constraints/int_enum/sort.rb +1 -4
- data/lib/gecoder/interface/constraints/set/cardinality.rb +6 -3
- data/lib/gecoder/interface/constraints/set/connection.rb +136 -0
- data/lib/gecoder/interface/constraints/set_enum/channel.rb +18 -0
- data/lib/gecoder/interface/constraints/set_enum/distinct.rb +61 -0
- data/lib/gecoder/interface/constraints/set_enum_constraints.rb +32 -0
- data/lib/gecoder/interface/constraints/set_var_constraints.rb +1 -0
- data/lib/gecoder/interface/enum_wrapper.rb +12 -3
- data/lib/gecoder/interface/model.rb +77 -56
- data/lib/gecoder/interface/search.rb +74 -5
- data/lib/gecoder/interface/variables.rb +117 -15
- data/lib/gecoder/version.rb +1 -1
- data/specs/binding_changes.rb +9 -5
- data/specs/bool_var.rb +8 -12
- data/specs/branch.rb +85 -19
- data/specs/constraints/arithmetic.rb +99 -71
- data/specs/constraints/bool_enum.rb +26 -18
- data/specs/constraints/boolean.rb +53 -49
- data/specs/constraints/cardinality.rb +33 -26
- data/specs/constraints/channel.rb +77 -6
- data/specs/constraints/connection.rb +352 -0
- data/specs/constraints/constraints.rb +10 -1
- data/specs/constraints/count.rb +79 -39
- data/specs/constraints/distinct.rb +128 -9
- data/specs/constraints/element.rb +26 -19
- data/specs/constraints/equality.rb +2 -1
- data/specs/constraints/int_domain.rb +19 -12
- data/specs/constraints/int_relation.rb +12 -6
- data/specs/constraints/linear.rb +30 -30
- data/specs/constraints/reification_sugar.rb +8 -4
- data/specs/constraints/set_domain.rb +24 -18
- data/specs/constraints/set_relation.rb +38 -23
- data/specs/constraints/sort.rb +12 -10
- data/specs/enum_wrapper.rb +9 -3
- data/specs/int_var.rb +8 -4
- data/specs/logging.rb +24 -0
- data/specs/model.rb +25 -7
- data/specs/search.rb +41 -1
- data/specs/set_var.rb +36 -7
- data/specs/spec_helper.rb +3 -10
- data/vendor/rust/rust/templates/FunctionDefinition.rusttpl +1 -1
- metadata +12 -3
- data/specs/tmp +0 -22
@@ -0,0 +1,32 @@
|
|
1
|
+
module Gecode
|
2
|
+
module SetEnumMethods
|
3
|
+
include Gecode::Constraints::LeftHandSideMethods
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
# Produces an expression for the lhs module.
|
8
|
+
def expression(params)
|
9
|
+
params.update(:lhs => self)
|
10
|
+
Constraints::SetEnum::Expression.new(@model, params)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# A module containing constraints that have enumerations of set variables as
|
15
|
+
# left hand side.
|
16
|
+
module Constraints::SetEnum
|
17
|
+
# Expressions with set enums as left hand sides.
|
18
|
+
class Expression < Gecode::Constraints::Expression
|
19
|
+
# Raises TypeError unless the left hand side is a set enum.
|
20
|
+
def initialize(model, params)
|
21
|
+
super
|
22
|
+
|
23
|
+
unless params[:lhs].respond_to? :to_set_var_array
|
24
|
+
raise TypeError, 'Must have set enum as left hand side.'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'gecoder/interface/constraints/set_enum/channel'
|
32
|
+
require 'gecoder/interface/constraints/set_enum/distinct'
|
@@ -45,10 +45,19 @@ module Gecode
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
module VariableEnumMethods
|
49
|
+
include EnumMethods
|
50
|
+
|
51
|
+
# Gets the values of all the variables in the enum.
|
52
|
+
def values
|
53
|
+
map{ |var| var.value }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
48
57
|
# A module containing the methods needed by enumerations containing int
|
49
58
|
# variables. Requires that it's included in an enumerable.
|
50
59
|
module IntEnumMethods
|
51
|
-
include
|
60
|
+
include VariableEnumMethods
|
52
61
|
|
53
62
|
# Returns an int variable array with all the bound variables.
|
54
63
|
def to_int_var_array
|
@@ -80,7 +89,7 @@ module Gecode
|
|
80
89
|
# A module containing the methods needed by enumerations containing boolean
|
81
90
|
# variables. Requires that it's included in an enumerable.
|
82
91
|
module BoolEnumMethods
|
83
|
-
include
|
92
|
+
include VariableEnumMethods
|
84
93
|
|
85
94
|
# Returns a bool variable array with all the bound variables.
|
86
95
|
def to_bool_var_array
|
@@ -99,7 +108,7 @@ module Gecode
|
|
99
108
|
# A module containing the methods needed by enumerations containing set
|
100
109
|
# variables. Requires that it's included in an enumerable.
|
101
110
|
module SetEnumMethods
|
102
|
-
include
|
111
|
+
include VariableEnumMethods
|
103
112
|
|
104
113
|
# Returns a set variable array with all the bound variables.
|
105
114
|
def to_set_var_array
|
@@ -1,15 +1,12 @@
|
|
1
1
|
module Gecode
|
2
2
|
# Model is the base class that all models must inherit from.
|
3
3
|
class Model
|
4
|
-
attr :constraints
|
5
|
-
protected :constraints
|
6
|
-
|
7
4
|
# Creates a new integer variable with the specified domain. The domain can
|
8
5
|
# either be a range or a number of elements.
|
9
6
|
def int_var(*domain_args)
|
10
|
-
|
11
|
-
index =
|
12
|
-
|
7
|
+
enum = domain_enum(*domain_args)
|
8
|
+
index = selected_space.new_int_vars(enum).first
|
9
|
+
FreeIntVar.new(self, index)
|
13
10
|
end
|
14
11
|
|
15
12
|
# Creates an array containing the specified number of integer variables
|
@@ -18,10 +15,10 @@ module Gecode
|
|
18
15
|
def int_var_array(count, *domain_args)
|
19
16
|
# TODO: Maybe the custom domain should be specified as an array instead?
|
20
17
|
|
21
|
-
|
18
|
+
enum = domain_enum(*domain_args)
|
22
19
|
variables = []
|
23
|
-
|
24
|
-
variables <<
|
20
|
+
selected_space.new_int_vars(enum, count).each do |index|
|
21
|
+
variables << FreeIntVar.new(self, index)
|
25
22
|
end
|
26
23
|
return wrap_enum(variables)
|
27
24
|
end
|
@@ -32,13 +29,12 @@ module Gecode
|
|
32
29
|
def int_var_matrix(row_count, col_count, *domain_args)
|
33
30
|
# TODO: Maybe the custom domain should be specified as an array instead?
|
34
31
|
|
35
|
-
|
36
|
-
indices =
|
37
|
-
row_count*col_count)
|
32
|
+
enum = domain_enum(*domain_args)
|
33
|
+
indices = selected_space.new_int_vars(enum, row_count*col_count)
|
38
34
|
rows = []
|
39
35
|
row_count.times do |i|
|
40
36
|
rows << indices[(i*col_count)...(i.succ*col_count)].map! do |index|
|
41
|
-
|
37
|
+
FreeIntVar.new(self, index)
|
42
38
|
end
|
43
39
|
end
|
44
40
|
return wrap_enum(Util::EnumMatrix.rows(rows, false))
|
@@ -46,14 +42,14 @@ module Gecode
|
|
46
42
|
|
47
43
|
# Creates a new boolean variable.
|
48
44
|
def bool_var(*domain_args)
|
49
|
-
index =
|
45
|
+
index = selected_space.new_bool_vars.first
|
50
46
|
FreeBoolVar.new(self, index)
|
51
47
|
end
|
52
48
|
|
53
49
|
# Creates an array containing the specified number of boolean variables.
|
54
50
|
def bool_var_array(count)
|
55
51
|
variables = []
|
56
|
-
|
52
|
+
selected_space.new_bool_vars(count).each do |index|
|
57
53
|
variables << FreeBoolVar.new(self, index)
|
58
54
|
end
|
59
55
|
return wrap_enum(variables)
|
@@ -62,7 +58,7 @@ module Gecode
|
|
62
58
|
# Creates a matrix containing the specified number rows and columns of
|
63
59
|
# boolean variables.
|
64
60
|
def bool_var_matrix(row_count, col_count)
|
65
|
-
indices =
|
61
|
+
indices = selected_space.new_bool_vars(row_count*col_count)
|
66
62
|
rows = []
|
67
63
|
row_count.times do |i|
|
68
64
|
rows << indices[(i*col_count)...(i.succ*col_count)].map! do |index|
|
@@ -81,7 +77,7 @@ module Gecode
|
|
81
77
|
def set_var(glb_domain, lub_domain, cardinality_range = nil)
|
82
78
|
check_set_bounds(glb_domain, lub_domain)
|
83
79
|
|
84
|
-
index =
|
80
|
+
index = selected_space.new_set_vars(glb_domain, lub_domain,
|
85
81
|
to_set_cardinality_range(cardinality_range)).first
|
86
82
|
FreeSetVar.new(self, index)
|
87
83
|
end
|
@@ -92,7 +88,7 @@ module Gecode
|
|
92
88
|
check_set_bounds(glb_domain, lub_domain)
|
93
89
|
|
94
90
|
variables = []
|
95
|
-
|
91
|
+
selected_space.new_set_vars(glb_domain, lub_domain,
|
96
92
|
to_set_cardinality_range(cardinality_range), count).each do |index|
|
97
93
|
variables << FreeSetVar.new(self, index)
|
98
94
|
end
|
@@ -106,7 +102,7 @@ module Gecode
|
|
106
102
|
cardinality_range = nil)
|
107
103
|
check_set_bounds(glb_domain, lub_domain)
|
108
104
|
|
109
|
-
indices =
|
105
|
+
indices = selected_space.new_set_vars(glb_domain, lub_domain,
|
110
106
|
to_set_cardinality_range(cardinality_range), row_count*col_count)
|
111
107
|
rows = []
|
112
108
|
row_count.times do |i|
|
@@ -117,53 +113,83 @@ module Gecode
|
|
117
113
|
return wrap_enum(Util::EnumMatrix.rows(rows, false))
|
118
114
|
end
|
119
115
|
|
120
|
-
# Retrieves the currently
|
116
|
+
# Retrieves the currently used space. Calling this method is only allowed
|
117
|
+
# when sanctioned by the model beforehand, e.g. when the model asks a
|
118
|
+
# constraint to post itself. Otherwise an RuntimeError is raised.
|
119
|
+
#
|
120
|
+
# The space returned by this method should never be stored, it should be
|
121
|
+
# rerequested from the model every time that it's needed.
|
121
122
|
def active_space
|
122
|
-
@
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
@base_space ||= Gecode::Raw::Space.new
|
123
|
+
unless @allow_space_access
|
124
|
+
raise 'Space access is restricted and the permission to access the ' +
|
125
|
+
'space has not been given.'
|
126
|
+
end
|
127
|
+
selected_space
|
128
128
|
end
|
129
129
|
|
130
130
|
# Adds the specified constraint to the model. Returns the newly added
|
131
131
|
# constraint.
|
132
132
|
def add_constraint(constraint)
|
133
|
-
|
133
|
+
add_interaction do
|
134
|
+
constraint.post
|
135
|
+
end
|
134
136
|
return constraint
|
135
137
|
end
|
136
138
|
|
139
|
+
# Adds a block containing something that interacts with Gecode to a queue
|
140
|
+
# where it is potentially executed.
|
141
|
+
def add_interaction(&block)
|
142
|
+
gecode_interaction_queue << block
|
143
|
+
end
|
144
|
+
|
145
|
+
# Allows the model's active space to be accessed while the block is
|
146
|
+
# executed. Don't use this unless you know what you're doing. Anything that
|
147
|
+
# the space is used for (such as bound variables) must be released before
|
148
|
+
# the block ends.
|
149
|
+
#
|
150
|
+
# Returns the result of the block.
|
151
|
+
def allow_space_access(&block)
|
152
|
+
# We store the old value so that nested calls don't become a problem, i.e.
|
153
|
+
# access is allowed as long as one call to this method is still on the
|
154
|
+
# stack.
|
155
|
+
old = @allow_space_access
|
156
|
+
@allow_space_access = true
|
157
|
+
res = yield
|
158
|
+
@allow_space_access = old
|
159
|
+
return res
|
160
|
+
end
|
161
|
+
|
137
162
|
protected
|
138
163
|
|
139
|
-
|
140
|
-
|
164
|
+
# Gets a queue of objects that can be posted to the model's active_space
|
165
|
+
# (by calling their post method).
|
166
|
+
def gecode_interaction_queue
|
167
|
+
@gecode_interaction_queue ||= []
|
141
168
|
end
|
142
169
|
|
143
170
|
private
|
144
171
|
|
145
|
-
# Returns
|
146
|
-
# given as a range or a number of elements. Raises ArgumentError
|
147
|
-
# arguments have been specified.
|
148
|
-
def
|
149
|
-
min = max = nil
|
172
|
+
# Returns an enumeration of the specified domain arguments, which can
|
173
|
+
# either be given as a range or a number of elements. Raises ArgumentError
|
174
|
+
# if no arguments have been specified.
|
175
|
+
def domain_enum(*domain_args)
|
150
176
|
if domain_args.empty?
|
151
177
|
raise ArgumentError, 'A domain has to be specified.'
|
152
178
|
elsif domain_args.size > 1
|
153
|
-
|
154
|
-
max = domain_args.max
|
179
|
+
return domain_args
|
155
180
|
else
|
156
181
|
element = domain_args.first
|
157
|
-
if element.respond_to?(:
|
182
|
+
if element.respond_to?(:first) and element.respond_to?(:last) and
|
158
183
|
element.respond_to?(:exclude_end?)
|
159
|
-
|
160
|
-
|
161
|
-
|
184
|
+
if element.exclude_end?
|
185
|
+
return element.first..(element.last - 1)
|
186
|
+
else
|
187
|
+
return element
|
188
|
+
end
|
162
189
|
else
|
163
|
-
|
190
|
+
return element..element
|
164
191
|
end
|
165
192
|
end
|
166
|
-
return min..max
|
167
193
|
end
|
168
194
|
|
169
195
|
# Transforms the argument to a set cardinality range, returns nil if the
|
@@ -196,20 +222,15 @@ module Gecode
|
|
196
222
|
end
|
197
223
|
end
|
198
224
|
|
199
|
-
#
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
# the variable before solving.
|
209
|
-
constraint = var.must_be.in domain_args
|
210
|
-
@constraints.delete(constraint).post
|
211
|
-
end
|
212
|
-
return var
|
225
|
+
# Retrieves the base from which searches are made.
|
226
|
+
def base_space
|
227
|
+
@base_space ||= Gecode::Raw::Space.new
|
228
|
+
end
|
229
|
+
|
230
|
+
# Retrieves the currently selected space, the one which constraints and
|
231
|
+
# variables should be bound to.
|
232
|
+
def selected_space
|
233
|
+
@active_space ||= base_space
|
213
234
|
end
|
214
235
|
end
|
215
236
|
end
|
@@ -36,20 +36,89 @@ module Gecode
|
|
36
36
|
self.reset!
|
37
37
|
end
|
38
38
|
|
39
|
+
# Finds the optimal solution. Optimality is defined by the provided block
|
40
|
+
# which is given one parameter, a solution to the problem. The block should
|
41
|
+
# constrain the solution so that that only "better" solutions can be new
|
42
|
+
# solutions. For instance if one wants to optimize a variable named price
|
43
|
+
# (accessible from the model) to be as low as possible then one should write
|
44
|
+
# the following.
|
45
|
+
#
|
46
|
+
# model.optimize! do |model, best_so_far|
|
47
|
+
# model.price.must < best_so_far.price.val
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# Returns nil if there is no solution.
|
51
|
+
def optimize!(&block)
|
52
|
+
next_space = nil
|
53
|
+
best_space = nil
|
54
|
+
bab = bab_engine
|
55
|
+
|
56
|
+
Model.constrain_proc = lambda do |home_space, best_space|
|
57
|
+
@active_space = best_space
|
58
|
+
yield(self, self)
|
59
|
+
@active_space = home_space
|
60
|
+
perform_queued_gecode_interactions
|
61
|
+
end
|
62
|
+
|
63
|
+
while not (next_space = bab.next).nil?
|
64
|
+
best_space = next_space
|
65
|
+
end
|
66
|
+
Model.constrain_proc = nil
|
67
|
+
return nil if best_space.nil?
|
68
|
+
return self
|
69
|
+
end
|
70
|
+
|
71
|
+
class <<self
|
72
|
+
def constrain_proc=(proc)
|
73
|
+
@constrain_proc = proc
|
74
|
+
end
|
75
|
+
|
76
|
+
def constrain(home, best)
|
77
|
+
if @constrain_proc.nil?
|
78
|
+
raise NotImplementedError, 'Constrain method not implemented.'
|
79
|
+
else
|
80
|
+
@constrain_proc.call(home, best)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
39
85
|
private
|
40
86
|
|
41
|
-
# Creates
|
42
|
-
# constraints first.
|
87
|
+
# Creates a depth first search engine for search, executing any
|
88
|
+
# unexecuted constraints first.
|
43
89
|
def dfs_engine
|
44
90
|
# Execute constraints.
|
45
|
-
|
46
|
-
|
91
|
+
perform_queued_gecode_interactions
|
92
|
+
|
93
|
+
# Construct the engine.
|
94
|
+
stop = Gecode::Raw::Search::Stop.new
|
95
|
+
Gecode::Raw::DFS.new(selected_space,
|
96
|
+
Gecode::Raw::Search::Config::MINIMAL_DISTANCE,
|
97
|
+
Gecode::Raw::Search::Config::ADAPTIVE_DISTANCE,
|
98
|
+
stop)
|
99
|
+
end
|
47
100
|
|
101
|
+
# Creates a branch and bound engine for optimization search, executing any
|
102
|
+
# unexecuted constraints first.
|
103
|
+
def bab_engine
|
104
|
+
# Execute constraints.
|
105
|
+
perform_queued_gecode_interactions
|
106
|
+
|
107
|
+
# Construct the engine.
|
48
108
|
stop = Gecode::Raw::Search::Stop.new
|
49
|
-
Gecode::Raw::
|
109
|
+
Gecode::Raw::BAB.new(selected_space,
|
50
110
|
Gecode::Raw::Search::Config::MINIMAL_DISTANCE,
|
51
111
|
Gecode::Raw::Search::Config::ADAPTIVE_DISTANCE,
|
52
112
|
stop)
|
53
113
|
end
|
114
|
+
|
115
|
+
# Executes any interactions with Gecode still waiting in the queue
|
116
|
+
# (emptying the queue) in the process.
|
117
|
+
def perform_queued_gecode_interactions
|
118
|
+
allow_space_access do
|
119
|
+
gecode_interaction_queue.each{ |con| con.call }
|
120
|
+
gecode_interaction_queue.clear # Empty the queue.
|
121
|
+
end
|
122
|
+
end
|
54
123
|
end
|
55
124
|
end
|
@@ -22,16 +22,7 @@ module Gecode
|
|
22
22
|
# class using the specified method in a space.
|
23
23
|
def Gecode::FreeVar(bound_class, space_bind_method)
|
24
24
|
clazz = Class.new(FreeVarBase)
|
25
|
-
clazz.class_eval <<-"end_method_definitions"
|
26
|
-
# Delegate methods we can't handle to the bound int variable if possible.
|
27
|
-
def method_missing(name, *args)
|
28
|
-
if #{bound_class}.instance_methods.include? name.to_s
|
29
|
-
bind.send(name, *args)
|
30
|
-
else
|
31
|
-
super
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
25
|
+
clazz.class_eval <<-"end_method_definitions"
|
35
26
|
# Binds the int variable to the currently active space of the model,
|
36
27
|
# returning the bound int variable.
|
37
28
|
def bind
|
@@ -51,6 +42,29 @@ module Gecode
|
|
51
42
|
"#<\#{self.class} \#{domain}>"
|
52
43
|
end
|
53
44
|
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# Delegates the method with the specified name to a method with the
|
49
|
+
# specified name when the variable is bound. If the bound method's name
|
50
|
+
# is nil then the same name as the new method's name is assumed.
|
51
|
+
def self.delegate(method_name, bound_method_name = nil)
|
52
|
+
bound_method_name = method_name if bound_method_name.nil?
|
53
|
+
module_eval <<-"end_code"
|
54
|
+
def \#{method_name}(*args)
|
55
|
+
@model.allow_space_access do
|
56
|
+
bind.method(:\#{bound_method_name}).call(*args)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end_code
|
60
|
+
end
|
61
|
+
|
62
|
+
# Sends the specified method name and arguments to the bound variable.
|
63
|
+
def send_bound(method_name, *args)
|
64
|
+
@model.allow_space_access do
|
65
|
+
bind.send(method_name, *args)
|
66
|
+
end
|
67
|
+
end
|
54
68
|
end_method_definitions
|
55
69
|
return clazz
|
56
70
|
end
|
@@ -58,10 +72,25 @@ module Gecode
|
|
58
72
|
# Int variables.
|
59
73
|
FreeIntVar = FreeVar(Gecode::Raw::IntVar, :int_var)
|
60
74
|
class FreeIntVar
|
75
|
+
delegate :min
|
76
|
+
delegate :max
|
77
|
+
delegate :size
|
78
|
+
delegate :width
|
79
|
+
delegate :degree
|
80
|
+
delegate :range?, :range
|
81
|
+
delegate :assigned?, :assigned
|
82
|
+
delegate :include?, :in
|
83
|
+
|
84
|
+
# Gets the value of the assigned integer variable (a fixnum).
|
85
|
+
def value
|
86
|
+
raise 'No value is assigned.' unless assigned?
|
87
|
+
send_bound(:val)
|
88
|
+
end
|
89
|
+
|
61
90
|
# Returns a string representation of the the range of the variable's domain.
|
62
91
|
def domain
|
63
92
|
if assigned?
|
64
|
-
"range: #{
|
93
|
+
"range: #{value.to_s}"
|
65
94
|
else
|
66
95
|
"range: #{min}..#{max}"
|
67
96
|
end
|
@@ -71,25 +100,98 @@ module Gecode
|
|
71
100
|
# Bool variables.
|
72
101
|
FreeBoolVar = FreeVar(Gecode::Raw::BoolVar, :bool_var)
|
73
102
|
class FreeBoolVar
|
103
|
+
delegate :assigned?, :assigned
|
104
|
+
|
105
|
+
# Gets the values in the assigned boolean variable (true or false).
|
106
|
+
def value
|
107
|
+
raise 'No value is assigned.' unless assigned?
|
108
|
+
send_bound(:val) == 1
|
109
|
+
end
|
110
|
+
|
74
111
|
# Returns a string representation of the the variable's domain.
|
75
112
|
def domain
|
76
113
|
if assigned?
|
77
|
-
|
114
|
+
value.to_s
|
78
115
|
else
|
79
116
|
'unassigned'
|
80
117
|
end
|
81
118
|
end
|
82
119
|
end
|
83
|
-
|
120
|
+
|
84
121
|
# Set variables.
|
85
122
|
FreeSetVar = FreeVar(Gecode::Raw::SetVar, :set_var)
|
86
123
|
class FreeSetVar
|
124
|
+
delegate :assigned?, :assigned
|
125
|
+
|
126
|
+
# Gets all the elements located in the greatest lower bound of the set.
|
127
|
+
def lower_bound
|
128
|
+
min = send_bound(:glbMin)
|
129
|
+
max = send_bound(:glbMax)
|
130
|
+
EnumerableView.new(min, max, send_bound(:glbSize)) do
|
131
|
+
(min..max).to_a.delete_if{ |e| not send_bound(:contains, e) }
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Gets all the elements located in the least upper bound of the set.
|
136
|
+
def upper_bound
|
137
|
+
min = send_bound(:lubMin)
|
138
|
+
max = send_bound(:lubMax)
|
139
|
+
EnumerableView.new(min, max, send_bound(:lubSize)) do
|
140
|
+
(min..max).to_a.delete_if{ |e| send_bound(:notContains, e) }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Gets the values in the assigned set variable (an enumerable).
|
145
|
+
def value
|
146
|
+
raise 'No value is assigned.' unless assigned?
|
147
|
+
lower_bound
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns a range containing the allowed values for the set's cardinality.
|
151
|
+
def cardinality
|
152
|
+
send_bound(:cardMin)..send_bound(:cardMax)
|
153
|
+
end
|
154
|
+
|
87
155
|
# Returns a string representation of the the variable's domain.
|
88
156
|
def domain
|
89
157
|
if assigned?
|
90
|
-
|
158
|
+
lower_bound.to_a.inspect
|
159
|
+
else
|
160
|
+
"glb-range: #{lower_bound.to_a.inspect}, lub-range: #{upper_bound.to_a.inspect}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Describes an immutable view of an enumerable.
|
166
|
+
class EnumerableView
|
167
|
+
attr :size
|
168
|
+
attr :min
|
169
|
+
attr :max
|
170
|
+
include Enumerable
|
171
|
+
|
172
|
+
# Constructs a view with the specified minimum, maximum and size. The block
|
173
|
+
# should construct an enumerable containing the elements of the set.
|
174
|
+
def initialize(min, max, size, &enum_constructor)
|
175
|
+
@min = min
|
176
|
+
@max = max
|
177
|
+
@size = size
|
178
|
+
@constructor = enum_constructor
|
179
|
+
@enum = nil
|
180
|
+
end
|
181
|
+
|
182
|
+
# Used by Enumerable.
|
183
|
+
def each(&block)
|
184
|
+
enum.each(&block)
|
185
|
+
end
|
186
|
+
|
187
|
+
private
|
188
|
+
|
189
|
+
# Gets the enumeration being viewed.
|
190
|
+
def enum
|
191
|
+
if @enum.nil?
|
192
|
+
@enum = @constructor.call
|
91
193
|
else
|
92
|
-
|
194
|
+
return @enum
|
93
195
|
end
|
94
196
|
end
|
95
197
|
end
|