gecoder-with-gecode 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,7 +8,7 @@ class DistinctSampleProblem < Gecode::Model
8
8
  def initialize
9
9
  @vars = int_var_array(2, 1)
10
10
  @sets = set_var_array(2, [], 0..2)
11
- branch_on wrap_enum(@sets)
11
+ branch_on @sets
12
12
  end
13
13
  end
14
14
 
@@ -1,25 +1,59 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper'
2
2
  require File.dirname(__FILE__) + '/constraint_helper'
3
3
 
4
- describe Gecode::Constraints::IntEnum::Extensional do
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
- @digits.must_be.in(@tuples, hash)
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
- an_instance_of(Gecode::Raw::IntVarArray),
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
- it 'should raise error if the right hand side does not contain integer tuples' do
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
- @invoke_options = lambda do |hash|
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
- it 'should raise error if the right hand side does not contain boolean tuples' do
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
@@ -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!
@@ -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
- end
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|
@@ -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 = [