gecoder-with-gecode 0.8.1-mswin32 → 0.8.2-mswin32
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 +10 -0
- data/lib/gecode.dll +0 -0
- data/lib/gecoder/bindings/bindings.rb +35 -0
- data/lib/gecoder/interface/constraints.rb +34 -0
- data/lib/gecoder/interface/constraints/bool/boolean.rb +7 -0
- data/lib/gecoder/interface/constraints/bool/channel.rb +23 -0
- data/lib/gecoder/interface/constraints/bool_enum/channel.rb +59 -0
- data/lib/gecoder/interface/constraints/bool_enum/extensional.rb +10 -9
- data/lib/gecoder/interface/constraints/bool_enum_constraints.rb +1 -0
- data/lib/gecoder/interface/constraints/bool_var_constraints.rb +1 -0
- data/lib/gecoder/interface/constraints/int/arithmetic.rb +78 -1
- data/lib/gecoder/interface/constraints/int/channel.rb +73 -0
- data/lib/gecoder/interface/constraints/int_enum/channel.rb +10 -2
- data/lib/gecoder/interface/constraints/int_enum/extensional.rb +12 -9
- data/lib/gecoder/interface/constraints/int_var_constraints.rb +1 -0
- data/lib/gecoder/interface/enum_wrapper.rb +3 -0
- data/lib/gecoder/interface/search.rb +34 -2
- data/lib/gecoder/version.rb +1 -1
- data/specs/constraints/arithmetic.rb +108 -41
- data/specs/constraints/boolean.rb +4 -0
- data/specs/constraints/channel.rb +235 -5
- data/specs/constraints/distinct.rb +1 -1
- data/specs/constraints/extensional.rb +46 -41
- data/specs/constraints/linear.rb +8 -0
- data/specs/enum_wrapper.rb +8 -1
- data/specs/search.rb +119 -61
- data/specs/spec_helper.rb +0 -28
- data/tasks/distribution.rake +2 -3
- metadata +10 -4
@@ -1,25 +1,59 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
2
2
|
require File.dirname(__FILE__) + '/constraint_helper'
|
3
3
|
|
4
|
-
|
4
|
+
# Assumes that @variables, @expected_array and @tuples are defined.
|
5
|
+
describe 'tuple constraint', :shared => true do
|
5
6
|
before do
|
6
|
-
@model = Gecode::Model.new
|
7
|
-
@tuples = [[1,7], [5,1]]
|
8
|
-
@digits = @model.int_var_array(2, 0..9)
|
9
|
-
@model.branch_on @digits
|
10
|
-
|
11
7
|
@invoke_options = lambda do |hash|
|
12
|
-
@
|
8
|
+
@variables.must_be.in(@tuples, hash)
|
13
9
|
@model.solve!
|
14
10
|
end
|
15
11
|
@expect_options = option_expectation do |strength, kind, reif_var|
|
16
12
|
Gecode::Raw.should_receive(:extensional).once.with(
|
17
13
|
an_instance_of(Gecode::Raw::Space),
|
18
|
-
|
14
|
+
@expected_array,
|
19
15
|
an_instance_of(Gecode::Raw::TupleSet), strength, kind)
|
20
16
|
end
|
21
17
|
end
|
22
18
|
|
19
|
+
it 'should not allow negation' do
|
20
|
+
lambda do
|
21
|
+
@variables.must_not_be.in @tuples
|
22
|
+
end.should raise_error(Gecode::MissingConstraintError)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should not allow empty tuples' do
|
26
|
+
lambda do
|
27
|
+
@variables.must_be.in []
|
28
|
+
end.should raise_error(ArgumentError)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should not allow tuples of sizes other than the number of variables' do
|
32
|
+
lambda do
|
33
|
+
@variables.must_be.in([@tuples.first * 2])
|
34
|
+
end.should raise_error(ArgumentError)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should raise error if the right hand side does not contain tuples of correct type' do
|
38
|
+
lambda do
|
39
|
+
size = @variables.size
|
40
|
+
@variables.must_be.in ['h'*size, 'i'*size]
|
41
|
+
end.should raise_error(TypeError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it_should_behave_like 'non-reifiable constraint'
|
45
|
+
end
|
46
|
+
|
47
|
+
describe Gecode::Constraints::IntEnum::Extensional do
|
48
|
+
before do
|
49
|
+
@model = Gecode::Model.new
|
50
|
+
@tuples = [[1,7], [5,1]]
|
51
|
+
@variables = @digits = @model.int_var_array(2, 0..9)
|
52
|
+
@model.branch_on @digits
|
53
|
+
|
54
|
+
@expected_array = an_instance_of Gecode::Raw::IntVarArray
|
55
|
+
end
|
56
|
+
|
23
57
|
it 'should constrain the domain of all variables' do
|
24
58
|
@digits.must_be.in @tuples
|
25
59
|
|
@@ -32,12 +66,6 @@ describe Gecode::Constraints::IntEnum::Extensional do
|
|
32
66
|
(found_solutions - @tuples).should be_empty
|
33
67
|
end
|
34
68
|
|
35
|
-
it 'should not allow negation' do
|
36
|
-
lambda do
|
37
|
-
@digits.must_not_be.in @tuples
|
38
|
-
end.should raise_error(Gecode::MissingConstraintError)
|
39
|
-
end
|
40
|
-
|
41
69
|
it 'should raise error if the right hand side is not an enumeration' do
|
42
70
|
lambda{ @digits.must_be.in 4711 }.should raise_error(TypeError)
|
43
71
|
end
|
@@ -46,30 +74,17 @@ describe Gecode::Constraints::IntEnum::Extensional do
|
|
46
74
|
lambda{ @digits.must_be.in [17, 4711] }.should raise_error(TypeError)
|
47
75
|
end
|
48
76
|
|
49
|
-
|
50
|
-
lambda{ @digits.must_be.in ['hello'] }.should raise_error(TypeError)
|
51
|
-
end
|
52
|
-
|
53
|
-
it_should_behave_like 'non-reifiable constraint'
|
77
|
+
it_should_behave_like 'tuple constraint'
|
54
78
|
end
|
55
79
|
|
56
80
|
describe Gecode::Constraints::BoolEnum::Extensional do
|
57
81
|
before do
|
58
82
|
@model = Gecode::Model.new
|
59
83
|
@tuples = [[true, false, true], [false, false, true]]
|
60
|
-
@bools = @model.bool_var_array(3)
|
84
|
+
@variables = @bools = @model.bool_var_array(3)
|
61
85
|
@model.branch_on @bools
|
62
86
|
|
63
|
-
@
|
64
|
-
@bools.must_be.in(@tuples, hash)
|
65
|
-
@model.solve!
|
66
|
-
end
|
67
|
-
@expect_options = option_expectation do |strength, kind, reif_var|
|
68
|
-
Gecode::Raw.should_receive(:extensional).once.with(
|
69
|
-
an_instance_of(Gecode::Raw::Space),
|
70
|
-
an_instance_of(Gecode::Raw::BoolVarArray),
|
71
|
-
an_instance_of(Gecode::Raw::TupleSet), strength, kind)
|
72
|
-
end
|
87
|
+
@expected_array = an_instance_of Gecode::Raw::BoolVarArray
|
73
88
|
end
|
74
89
|
|
75
90
|
it 'should constrain the domain of all variables' do
|
@@ -84,12 +99,6 @@ describe Gecode::Constraints::BoolEnum::Extensional do
|
|
84
99
|
(found_solutions - @tuples).should be_empty
|
85
100
|
end
|
86
101
|
|
87
|
-
it 'should not allow negation' do
|
88
|
-
lambda do
|
89
|
-
@bools.must_not_be.in @tuples
|
90
|
-
end.should raise_error(Gecode::MissingConstraintError)
|
91
|
-
end
|
92
|
-
|
93
102
|
it 'should raise error if the right hand side is not an enumeration' do
|
94
103
|
lambda{ @bools.must_be.in true }.should raise_error(TypeError)
|
95
104
|
end
|
@@ -98,9 +107,5 @@ describe Gecode::Constraints::BoolEnum::Extensional do
|
|
98
107
|
lambda{ @bools.must_be.in [true, false] }.should raise_error(TypeError)
|
99
108
|
end
|
100
109
|
|
101
|
-
|
102
|
-
lambda{ @bools.must_be.in ['hello'] }.should raise_error(TypeError)
|
103
|
-
end
|
104
|
-
|
105
|
-
it_should_behave_like 'non-reifiable constraint'
|
110
|
+
it_should_behave_like 'tuple constraint'
|
106
111
|
end
|
data/specs/constraints/linear.rb
CHANGED
@@ -240,6 +240,14 @@ describe Gecode::Constraints::Int::Linear, '(with booleans)' do
|
|
240
240
|
(x + y).should equal(1)
|
241
241
|
end
|
242
242
|
|
243
|
+
it 'should handle singe booleans as left hand side' do
|
244
|
+
@x.must == @y + 1
|
245
|
+
sol = @model.solve!
|
246
|
+
x = sol.x.value.to_i
|
247
|
+
y = sol.y.value.to_i
|
248
|
+
x.should equal(y + 1)
|
249
|
+
end
|
250
|
+
|
243
251
|
it 'should handle variables as right hand side' do
|
244
252
|
(@x + @y).must == @z
|
245
253
|
sol = @model.solve!
|
data/specs/enum_wrapper.rb
CHANGED
@@ -40,6 +40,13 @@ describe Gecode::Model, ' (enum wrapping)' do
|
|
40
40
|
end.should raise_error(ArgumentError)
|
41
41
|
end
|
42
42
|
|
43
|
+
it 'should not allow wrapping a wrapped enumerable' do
|
44
|
+
lambda do
|
45
|
+
enum = [@bool]
|
46
|
+
@model.wrap_enum(@model.wrap_enum(enum))
|
47
|
+
end.should raise_error(ArgumentError)
|
48
|
+
end
|
49
|
+
|
43
50
|
it 'should not allow enumerables without variables or fixnums to be wrapped' do
|
44
51
|
lambda do
|
45
52
|
@model.wrap_enum(['foo'])
|
@@ -119,4 +126,4 @@ describe Gecode::FixnumEnumMethods do
|
|
119
126
|
it 'should compute the smallest domain range' do
|
120
127
|
@enum.domain_range.should == (7..4711)
|
121
128
|
end
|
122
|
-
end
|
129
|
+
end
|
data/specs/search.rb
CHANGED
@@ -65,6 +65,12 @@ describe Gecode::Model, ' (with multiple solutions)' do
|
|
65
65
|
s.var.should have_domain(@solved_domain)
|
66
66
|
end
|
67
67
|
end
|
68
|
+
|
69
|
+
it 'should update the search statistics before yielding to #solution' do
|
70
|
+
@model.solution do |s|
|
71
|
+
@model.search_stats.should_not be_nil
|
72
|
+
end
|
73
|
+
end
|
68
74
|
|
69
75
|
it 'should only evaluate the block for one solution in #solution' do
|
70
76
|
i = 0
|
@@ -83,6 +89,18 @@ describe Gecode::Model, ' (with multiple solutions)' do
|
|
83
89
|
end
|
84
90
|
Set.new(solutions).should == Set.new([2,3])
|
85
91
|
end
|
92
|
+
|
93
|
+
it 'should update the search statistics before yielding to #each_solution' do
|
94
|
+
solutions = []
|
95
|
+
old_stats = @model.search_stats
|
96
|
+
old_stats.should be_nil
|
97
|
+
@model.each_solution do |s|
|
98
|
+
solutions << s.var.value
|
99
|
+
@model.search_stats.should_not == old_stats
|
100
|
+
@model.search_stats.should_not be_nil
|
101
|
+
old_stats = @model.search_stats
|
102
|
+
end
|
103
|
+
end
|
86
104
|
end
|
87
105
|
|
88
106
|
describe Gecode::Model, ' (after #solve!)' do
|
@@ -114,6 +132,15 @@ describe Gecode::Model, ' (after #solve!)' do
|
|
114
132
|
enum[2].first.should have_domain(@solved_domain)
|
115
133
|
enum[3][1][:b].should have_domain(@solved_domain)
|
116
134
|
end
|
135
|
+
|
136
|
+
it 'should have updated the search statistics' do
|
137
|
+
stats = @model.search_stats
|
138
|
+
stats[:propagations].should == 0
|
139
|
+
stats[:failures].should == 0
|
140
|
+
stats[:clones].should_not be_nil
|
141
|
+
stats[:commits].should_not be_nil
|
142
|
+
stats[:memory].should > 0
|
143
|
+
end
|
117
144
|
end
|
118
145
|
|
119
146
|
describe 'reset model', :shared => true do
|
@@ -126,6 +153,10 @@ describe 'reset model', :shared => true do
|
|
126
153
|
enum[2].first.should have_domain(@reset_domain)
|
127
154
|
enum[3][1][:b].should have_domain(@reset_domain)
|
128
155
|
end
|
156
|
+
|
157
|
+
it 'should have cleared the search statistics' do
|
158
|
+
@model.search_stats.should be_nil
|
159
|
+
end
|
129
160
|
end
|
130
161
|
|
131
162
|
describe Gecode::Model, ' (after #reset!)' do
|
@@ -205,66 +236,6 @@ describe Gecode::Model, '(optimization search)' do
|
|
205
236
|
solution.z.value.should == 25
|
206
237
|
end
|
207
238
|
|
208
|
-
it 'should support maximizing singe variables given as symbols' do
|
209
|
-
solution = SampleOptimizationProblem.new.maximize! :z
|
210
|
-
solution.should_not be_nil
|
211
|
-
solution.x.value.should == 5
|
212
|
-
solution.y.value.should == 5
|
213
|
-
solution.z.value.should == 25
|
214
|
-
end
|
215
|
-
|
216
|
-
it 'should support maximizing singe variables given as strings' do
|
217
|
-
solution = SampleOptimizationProblem.new.maximize! 'z'
|
218
|
-
solution.should_not be_nil
|
219
|
-
solution.x.value.should == 5
|
220
|
-
solution.y.value.should == 5
|
221
|
-
solution.z.value.should == 25
|
222
|
-
end
|
223
|
-
|
224
|
-
it 'should raise error if maximize! is given a non-existing method' do
|
225
|
-
lambda do
|
226
|
-
SampleOptimizationProblem.new.maximize! :does_not_exist
|
227
|
-
end.should raise_error(NameError)
|
228
|
-
end
|
229
|
-
|
230
|
-
it 'should raise error if maximize! is given a method that does not return an integer variable' do
|
231
|
-
lambda do
|
232
|
-
SampleOptimizationProblem.new.maximize! :object_id
|
233
|
-
end.should raise_error(ArgumentError)
|
234
|
-
end
|
235
|
-
|
236
|
-
it 'should support minimizing singe variables given as symbols' do
|
237
|
-
problem = SampleOptimizationProblem.new
|
238
|
-
problem.z.must > 2
|
239
|
-
solution = problem.minimize! :x
|
240
|
-
solution.should_not be_nil
|
241
|
-
solution.x.value.should == 1
|
242
|
-
solution.y.value.should == 3
|
243
|
-
solution.z.value.should == 3
|
244
|
-
end
|
245
|
-
|
246
|
-
it 'should support minimizing singe variables given as strings' do
|
247
|
-
problem = SampleOptimizationProblem.new
|
248
|
-
problem.z.must > 2
|
249
|
-
solution = problem.minimize! 'x'
|
250
|
-
solution.should_not be_nil
|
251
|
-
solution.x.value.should == 1
|
252
|
-
solution.y.value.should == 3
|
253
|
-
solution.z.value.should == 3
|
254
|
-
end
|
255
|
-
|
256
|
-
it 'should raise error if minimize! is given a non-existing method' do
|
257
|
-
lambda do
|
258
|
-
SampleOptimizationProblem.new.minimize! :does_not_exist
|
259
|
-
end.should raise_error(NameError)
|
260
|
-
end
|
261
|
-
|
262
|
-
it 'should raise error if minimize! is given a method that does not return an integer variable' do
|
263
|
-
lambda do
|
264
|
-
SampleOptimizationProblem.new.minimize! :object_id
|
265
|
-
end.should raise_error(ArgumentError)
|
266
|
-
end
|
267
|
-
|
268
239
|
it 'should not be bothered by garbage collecting' do
|
269
240
|
# This goes through 400+ spaces.
|
270
241
|
solution = SampleOptimizationProblem2.new.optimize! do |model, best_so_far|
|
@@ -303,4 +274,91 @@ describe Gecode::Model, '(optimization search)' do
|
|
303
274
|
solution.y.value.should == 5
|
304
275
|
solution.z.value.should == 25
|
305
276
|
end
|
306
|
-
|
277
|
+
|
278
|
+
it 'should update the search statistics' do
|
279
|
+
model = SampleOptimizationProblem.new
|
280
|
+
solution = model.maximize! :z
|
281
|
+
|
282
|
+
stats = model.search_stats
|
283
|
+
stats.should_not be_nil
|
284
|
+
stats[:propagations].should be_between(1, 100)
|
285
|
+
stats[:failures].should be_between(1, 100)
|
286
|
+
stats[:clones].should_not be_nil
|
287
|
+
stats[:commits].should_not be_nil
|
288
|
+
stats[:memory].should > 0
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
describe 'single variable optimization', :shared => true do
|
293
|
+
it "should support #{@method_name} having the variable given as a symbol" do
|
294
|
+
solution = @model.method(@method_name).call(@variable_name.to_sym)
|
295
|
+
@expect_to_be_correct.call(solution)
|
296
|
+
end
|
297
|
+
|
298
|
+
it "should support #{@method_name} having the variable given as a string" do
|
299
|
+
solution = @model.method(@method_name).call(@variable_name.to_s)
|
300
|
+
@expect_to_be_correct.call(solution)
|
301
|
+
end
|
302
|
+
|
303
|
+
it "should raise error if #{@method_name} is given a non-existing method" do
|
304
|
+
lambda do
|
305
|
+
SampleOptimizationProblem.new.method(@method_name).call(:does_not_exist)
|
306
|
+
end.should raise_error(NameError)
|
307
|
+
end
|
308
|
+
|
309
|
+
it "should raise error if #{@method_name} is given a method that does not return an integer variable" do
|
310
|
+
lambda do
|
311
|
+
SampleOptimizationProblem.new.method(@method_name).call(:object_id)
|
312
|
+
end.should raise_error(ArgumentError)
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'should update the search statistics' do
|
316
|
+
@model.method(@method_name).call(@variable_name.to_sym)
|
317
|
+
|
318
|
+
stats = @model.search_stats
|
319
|
+
stats.should_not be_nil
|
320
|
+
stats[:propagations].should be_between(1, 100)
|
321
|
+
stats[:failures].should be_between(1, 100)
|
322
|
+
stats[:clones].should_not be_nil
|
323
|
+
stats[:commits].should_not be_nil
|
324
|
+
stats[:memory].should > 0
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
describe Gecode::Model, '(single variable minimization)' do
|
329
|
+
before do
|
330
|
+
@method_name = 'minimize!'
|
331
|
+
@variable_name = 'x'
|
332
|
+
|
333
|
+
@model = SampleOptimizationProblem.new
|
334
|
+
@model.z.must > 2
|
335
|
+
|
336
|
+
@expect_to_be_correct = lambda do |solution|
|
337
|
+
solution.should_not be_nil
|
338
|
+
solution.x.value.should == 1
|
339
|
+
solution.y.value.should == 3
|
340
|
+
solution.z.value.should == 3
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
it_should_behave_like 'single variable optimization'
|
345
|
+
end
|
346
|
+
|
347
|
+
describe Gecode::Model, '(single variable maximization)' do
|
348
|
+
before do
|
349
|
+
@method_name = 'maximize!'
|
350
|
+
@variable_name = 'z'
|
351
|
+
|
352
|
+
@model = SampleOptimizationProblem.new
|
353
|
+
|
354
|
+
@expect_to_be_correct = lambda do |solution|
|
355
|
+
solution.should_not be_nil
|
356
|
+
solution.x.value.should == 5
|
357
|
+
solution.y.value.should == 5
|
358
|
+
solution.z.value.should == 25
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
it_should_behave_like 'single variable optimization'
|
363
|
+
end
|
364
|
+
|
data/specs/spec_helper.rb
CHANGED
@@ -58,34 +58,6 @@ module CustomVarMatchers
|
|
58
58
|
def have_bounds(expected_glb, expected_lub)
|
59
59
|
HaveBounds.new(expected_glb, expected_lub)
|
60
60
|
end
|
61
|
-
|
62
|
-
class IsAlias
|
63
|
-
def initialize(expected)
|
64
|
-
@expected = expected.to_a
|
65
|
-
end
|
66
|
-
|
67
|
-
def matches?(target)
|
68
|
-
@target = target
|
69
|
-
return false unless @target.size == @expected.size
|
70
|
-
@expected.each do |element|
|
71
|
-
return false unless @target.in(element)
|
72
|
-
end
|
73
|
-
return true
|
74
|
-
end
|
75
|
-
|
76
|
-
def failure_message
|
77
|
-
"expected #{@target.inspect} to be an alias of #{@expected.inspect}"
|
78
|
-
end
|
79
|
-
|
80
|
-
def negative_failure_message
|
81
|
-
"expected #{@target.inspect} not to be an alias of #{@expected.inspect}"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# Tests whether a method with a specified name is the alias of another.
|
86
|
-
def is_alias_of(expected)
|
87
|
-
HaveDomain.new(expected)
|
88
|
-
end
|
89
61
|
end
|
90
62
|
|
91
63
|
Spec::Runner.configure do |config|
|
data/tasks/distribution.rake
CHANGED
@@ -54,7 +54,7 @@ spec = Gem::Specification.new do |s|
|
|
54
54
|
s.files = FileList[
|
55
55
|
'[A-Z]*',
|
56
56
|
'lib/**/*.rb',
|
57
|
-
'example
|
57
|
+
'example/**/*.rb',
|
58
58
|
'src/**/*',
|
59
59
|
'vendor/rust/**/*',
|
60
60
|
'tasks/**/*',
|
@@ -136,8 +136,7 @@ end
|
|
136
136
|
|
137
137
|
desc 'Publish packages on RubyForge'
|
138
138
|
task :publish_packages => [:publish_gecoder_packages,
|
139
|
-
:publish_gecoder_with_gecode_packages
|
140
|
-
:publish_gecoder_with_gecode_mswin32_packages]
|
139
|
+
:publish_gecoder_with_gecode_packages]
|
141
140
|
|
142
141
|
# Files included in the vanilla Gecode/R release.
|
143
142
|
vanilla_release_files = [
|