gecoder 0.8.1 → 0.8.2
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/gecoder/bindings/bindings.rb +35 -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/constraints.rb +34 -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 +5 -7
- metadata +10 -4
@@ -17,6 +17,55 @@ class ArithmeticSampleProblem < Gecode::Model
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
# Construct a method placing expectations for an arithmetic constraint with the
|
21
|
+
# specified arity (number of variables before must) and the specified name in
|
22
|
+
# Gecode.
|
23
|
+
def arithmetic_expectation(gecode_name, arity)
|
24
|
+
lambda do |relation, rhs, strength, kind, reif_var, negated|
|
25
|
+
# Construct the arguments expected to be passed to the Gecode variant of
|
26
|
+
# the constraint.
|
27
|
+
rhs = an_instance_of(Gecode::Raw::IntVar) if rhs.respond_to? :bind
|
28
|
+
expected_gecode_arguments = [an_instance_of(Gecode::Raw::Space)]
|
29
|
+
arity.times do
|
30
|
+
expected_gecode_arguments << an_instance_of(Gecode::Raw::IntVar)
|
31
|
+
end
|
32
|
+
can_use_single_gecode_constraint = reif_var.nil? && !negated &&
|
33
|
+
relation == Gecode::Raw::IRT_EQ && !rhs.kind_of?(Fixnum)
|
34
|
+
if can_use_single_gecode_constraint
|
35
|
+
expected_gecode_arguments << rhs
|
36
|
+
else
|
37
|
+
expected_gecode_arguments << an_instance_of(Gecode::Raw::IntVar)
|
38
|
+
end
|
39
|
+
expected_gecode_arguments.concat([strength, kind])
|
40
|
+
|
41
|
+
# Create the actual method producing the expectation.
|
42
|
+
@model.allow_space_access do
|
43
|
+
if reif_var.nil?
|
44
|
+
if can_use_single_gecode_constraint
|
45
|
+
Gecode::Raw.should_receive(gecode_name).once.with(
|
46
|
+
*expected_gecode_arguments)
|
47
|
+
Gecode::Raw.should_receive(:rel).exactly(0).times
|
48
|
+
else
|
49
|
+
Gecode::Raw.should_receive(gecode_name).once.with(
|
50
|
+
*expected_gecode_arguments)
|
51
|
+
Gecode::Raw.should_receive(:rel).once.with(
|
52
|
+
an_instance_of(Gecode::Raw::Space),
|
53
|
+
an_instance_of(Gecode::Raw::IntVar),
|
54
|
+
relation, rhs, strength, kind)
|
55
|
+
end
|
56
|
+
else
|
57
|
+
Gecode::Raw.should_receive(gecode_name).once.with(
|
58
|
+
*expected_gecode_arguments)
|
59
|
+
Gecode::Raw.should_receive(:rel).once.with(
|
60
|
+
an_instance_of(Gecode::Raw::Space),
|
61
|
+
an_instance_of(Gecode::Raw::IntVar), relation, rhs,
|
62
|
+
an_instance_of(Gecode::Raw::BoolVar),
|
63
|
+
strength, kind)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
20
69
|
# Requires @stub, @target, @model and @expect.
|
21
70
|
describe 'arithmetic constraint', :shared => true do
|
22
71
|
before do
|
@@ -228,46 +277,7 @@ describe Gecode::Constraints::Int::Arithmetic, ' (multiplication)' do
|
|
228
277
|
@stub = @var * @var2
|
229
278
|
@target = @model.var3
|
230
279
|
|
231
|
-
|
232
|
-
@expect = lambda do |relation, rhs, strength, kind, reif_var, negated|
|
233
|
-
@model.allow_space_access do
|
234
|
-
rhs = an_instance_of(Gecode::Raw::IntVar) if rhs.respond_to? :bind
|
235
|
-
if reif_var.nil?
|
236
|
-
if !negated and relation == Gecode::Raw::IRT_EQ and
|
237
|
-
!rhs.kind_of? Fixnum
|
238
|
-
Gecode::Raw.should_receive(:mult).once.with(
|
239
|
-
an_instance_of(Gecode::Raw::Space),
|
240
|
-
an_instance_of(Gecode::Raw::IntVar),
|
241
|
-
an_instance_of(Gecode::Raw::IntVar),
|
242
|
-
rhs, strength, kind)
|
243
|
-
Gecode::Raw.should_receive(:rel).exactly(0).times
|
244
|
-
else
|
245
|
-
Gecode::Raw.should_receive(:mult).once.with(
|
246
|
-
an_instance_of(Gecode::Raw::Space),
|
247
|
-
an_instance_of(Gecode::Raw::IntVar),
|
248
|
-
an_instance_of(Gecode::Raw::IntVar),
|
249
|
-
an_instance_of(Gecode::Raw::IntVar),
|
250
|
-
strength, kind)
|
251
|
-
Gecode::Raw.should_receive(:rel).once.with(
|
252
|
-
an_instance_of(Gecode::Raw::Space),
|
253
|
-
an_instance_of(Gecode::Raw::IntVar),
|
254
|
-
relation, rhs, strength, kind)
|
255
|
-
end
|
256
|
-
else
|
257
|
-
Gecode::Raw.should_receive(:mult).once.with(
|
258
|
-
an_instance_of(Gecode::Raw::Space),
|
259
|
-
an_instance_of(Gecode::Raw::IntVar),
|
260
|
-
an_instance_of(Gecode::Raw::IntVar),
|
261
|
-
an_instance_of(Gecode::Raw::IntVar),
|
262
|
-
strength, kind)
|
263
|
-
Gecode::Raw.should_receive(:rel).once.with(
|
264
|
-
an_instance_of(Gecode::Raw::Space),
|
265
|
-
an_instance_of(Gecode::Raw::IntVar), relation, rhs,
|
266
|
-
an_instance_of(Gecode::Raw::BoolVar),
|
267
|
-
strength, kind)
|
268
|
-
end
|
269
|
-
end
|
270
|
-
end
|
280
|
+
@expect = arithmetic_expectation(:mult, 2)
|
271
281
|
end
|
272
282
|
|
273
283
|
it 'should constrain the value of the multiplication' do
|
@@ -281,4 +291,61 @@ describe Gecode::Constraints::Int::Arithmetic, ' (multiplication)' do
|
|
281
291
|
end
|
282
292
|
|
283
293
|
it_should_behave_like 'arithmetic constraint'
|
284
|
-
end
|
294
|
+
end
|
295
|
+
|
296
|
+
describe Gecode::Constraints::Int::Arithmetic, ' (squared)' do
|
297
|
+
before do
|
298
|
+
@model = ArithmeticSampleProblem.new
|
299
|
+
@var = @model.var
|
300
|
+
@stub = @var.squared
|
301
|
+
@target = @model.var2
|
302
|
+
|
303
|
+
@expect = arithmetic_expectation(:sqr, 1)
|
304
|
+
end
|
305
|
+
|
306
|
+
it 'should constrain the value of the variable squared' do
|
307
|
+
@var.squared.must == 9
|
308
|
+
sol = @model.solve!
|
309
|
+
sol.var.value.abs.should == 3
|
310
|
+
end
|
311
|
+
|
312
|
+
it_should_behave_like 'arithmetic constraint'
|
313
|
+
end
|
314
|
+
|
315
|
+
describe Gecode::Constraints::Int::Arithmetic, ' (square root)' do
|
316
|
+
before do
|
317
|
+
@model = ArithmeticSampleProblem.new
|
318
|
+
@var = @model.var
|
319
|
+
@stub = @var.square_root
|
320
|
+
@target = @model.var2
|
321
|
+
|
322
|
+
@expect = arithmetic_expectation(:sqrt, 1)
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'should constrain the square root of the variable' do
|
326
|
+
@var.square_root.must == 3
|
327
|
+
sol = @model.solve!
|
328
|
+
Math.sqrt(sol.var.value).floor.should == 3
|
329
|
+
end
|
330
|
+
|
331
|
+
it 'should constrain the square root of the variable (2)' do
|
332
|
+
@var.square_root.must == 0
|
333
|
+
sol = @model.solve!
|
334
|
+
Math.sqrt(sol.var.value).floor.should == 0
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'should constrain the square root of the variable (3)' do
|
338
|
+
@var.must < 0
|
339
|
+
@var.square_root.must == 0
|
340
|
+
@model.solve!.should be_nil
|
341
|
+
end
|
342
|
+
|
343
|
+
it 'should round down the square root' do
|
344
|
+
@var.must > 4
|
345
|
+
@var.square_root.must == 2
|
346
|
+
sol = @model.solve!
|
347
|
+
sol.var.value.should be_between(5,8)
|
348
|
+
end
|
349
|
+
|
350
|
+
it_should_behave_like 'arithmetic constraint'
|
351
|
+
end
|
@@ -228,5 +228,9 @@ describe Gecode::Constraints::Bool do
|
|
228
228
|
sol.b3.value.should be_true
|
229
229
|
end
|
230
230
|
|
231
|
+
it 'should raise error on right hand sides of the wrong type' do
|
232
|
+
lambda{ @b1.must == 'hello' }.should raise_error(TypeError)
|
233
|
+
end
|
234
|
+
|
231
235
|
it_should_behave_like 'reifiable constraint'
|
232
236
|
end
|
@@ -16,6 +16,20 @@ class ChannelSampleProblem < Gecode::Model
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
class BoolChannelSampleProblem < Gecode::Model
|
20
|
+
attr :bool_enum
|
21
|
+
attr :bool
|
22
|
+
attr :int
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@bool_enum = bool_var_array(4)
|
26
|
+
@int = int_var(0..3)
|
27
|
+
@bool = bool_var
|
28
|
+
|
29
|
+
branch_on wrap_enum([@int])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
19
33
|
describe Gecode::Constraints::IntEnum::Channel, ' (two int enums)' do
|
20
34
|
before do
|
21
35
|
@model = ChannelSampleProblem.new
|
@@ -67,13 +81,21 @@ describe Gecode::Constraints::IntEnum::Channel, ' (one int enum and one set enum
|
|
67
81
|
@model = ChannelSampleProblem.new
|
68
82
|
@positions = @model.positions
|
69
83
|
@sets = @model.sets
|
84
|
+
|
85
|
+
@invoke_options = lambda do |hash|
|
86
|
+
@positions.must.channel @sets, hash
|
87
|
+
@model.solve!
|
88
|
+
end
|
89
|
+
@expect_options = option_expectation do |strength, kind, reif_var|
|
90
|
+
Gecode::Raw.should_receive(:channel).once.with(
|
91
|
+
an_instance_of(Gecode::Raw::Space),
|
92
|
+
an_instance_of(Gecode::Raw::IntVarArray),
|
93
|
+
an_instance_of(Gecode::Raw::SetVarArray))
|
94
|
+
end
|
70
95
|
end
|
71
96
|
|
72
97
|
it 'should translate into a channel constraint' do
|
73
|
-
|
74
|
-
an_instance_of(Gecode::Raw::Space),
|
75
|
-
an_instance_of(Gecode::Raw::IntVarArray),
|
76
|
-
an_instance_of(Gecode::Raw::SetVarArray))
|
98
|
+
@expect_options.call({})
|
77
99
|
@positions.must.channel @sets
|
78
100
|
@model.solve!
|
79
101
|
end
|
@@ -87,6 +109,8 @@ describe Gecode::Constraints::IntEnum::Channel, ' (one int enum and one set enum
|
|
87
109
|
sets[position].value.should include(i)
|
88
110
|
end
|
89
111
|
end
|
112
|
+
|
113
|
+
it_should_behave_like 'non-reifiable set constraint'
|
90
114
|
end
|
91
115
|
|
92
116
|
describe Gecode::Constraints::SetEnum, ' (channel with set as left hand side)' do
|
@@ -123,4 +147,210 @@ describe Gecode::Constraints::SetEnum, ' (channel with set as left hand side)' d
|
|
123
147
|
end
|
124
148
|
|
125
149
|
it_should_behave_like 'non-reifiable set constraint'
|
126
|
-
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Requires @model, @bool and @int. Also requires @place_constraint which is a
|
153
|
+
# method that takes five variables: a boolean variable, an integer variable,
|
154
|
+
# the name of the equality method to use, whether or not the constraint should
|
155
|
+
# be negated and a hash of options, and places the channel constraint on them.
|
156
|
+
describe 'channel constraint between one int and one bool variable', :shared => true do
|
157
|
+
before do
|
158
|
+
@invoke_options = lambda do |hash|
|
159
|
+
@place_constraint.call(@bool, @int, :==, false, hash)
|
160
|
+
@model.solve!
|
161
|
+
end
|
162
|
+
@expect_options = option_expectation do |strength, kind, reif_var|
|
163
|
+
Gecode::Raw.should_receive(:channel).once.with(
|
164
|
+
an_instance_of(Gecode::Raw::Space),
|
165
|
+
an_instance_of(Gecode::Raw::IntVar),
|
166
|
+
an_instance_of(Gecode::Raw::BoolVar),
|
167
|
+
strength, kind)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
([:==] + Gecode::Constraints::Util::COMPARISON_ALIASES[:==]).each do |ali|
|
172
|
+
it "should translate #{ali} into a channel constraint" do
|
173
|
+
@expect_options.call({})
|
174
|
+
@place_constraint.call(@bool, @int, ali, false, {})
|
175
|
+
@model.solve!
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should constrain the int variable to be 1 when the boolean variable is true' do
|
180
|
+
@bool.must_be.true
|
181
|
+
@place_constraint.call(@bool, @int, :==, false, {})
|
182
|
+
@model.solve!
|
183
|
+
@int.value.should == 1
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'should constrain the int variable to be 0 when the boolean variable is false' do
|
187
|
+
@bool.must_be.false
|
188
|
+
@place_constraint.call(@bool, @int, :==, false, {})
|
189
|
+
@model.solve!
|
190
|
+
@int.value.should == 0
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'should not allow negation' do
|
194
|
+
lambda do
|
195
|
+
@place_constraint.call(@bool, @int, :==, true, {})
|
196
|
+
end.should raise_error(Gecode::MissingConstraintError)
|
197
|
+
end
|
198
|
+
|
199
|
+
it_should_behave_like 'non-reifiable constraint'
|
200
|
+
end
|
201
|
+
|
202
|
+
describe Gecode::Constraints::Int::Channel, ' (one int and one bool variable)' do
|
203
|
+
before do
|
204
|
+
@model = BoolChannelSampleProblem.new
|
205
|
+
@bool = @model.bool_var
|
206
|
+
@int = @model.int_var
|
207
|
+
|
208
|
+
@place_constraint = lambda do |bool, int, equals_method_name, negate, options|
|
209
|
+
if negate
|
210
|
+
int.must_not.method(equals_method_name).call(bool, options)
|
211
|
+
else
|
212
|
+
int.must.method(equals_method_name).call(bool, options)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'should not shadow linear boolean constraints' do
|
218
|
+
lambda do
|
219
|
+
(@bool + @bool).must == @bool
|
220
|
+
@model.solve!
|
221
|
+
end.should_not raise_error
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'should raise error for unsupported right hand sides' do
|
225
|
+
lambda{ @int.must == 'hello' }.should raise_error(TypeError)
|
226
|
+
end
|
227
|
+
|
228
|
+
it_should_behave_like 'channel constraint between one int and one bool variable'
|
229
|
+
end
|
230
|
+
|
231
|
+
describe Gecode::Constraints::Int::Channel, ' (one bool and one int variable)' do
|
232
|
+
before do
|
233
|
+
@model = BoolChannelSampleProblem.new
|
234
|
+
@bool = @model.bool_var
|
235
|
+
@int = @model.int_var
|
236
|
+
|
237
|
+
@place_constraint = lambda do |bool, int, equals_method_name, negate, options|
|
238
|
+
if negate
|
239
|
+
bool.must_not.method(equals_method_name).call(int, options)
|
240
|
+
else
|
241
|
+
bool.must.method(equals_method_name).call(int, options)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'should not shadow linear boolean constraints' do
|
247
|
+
lambda do
|
248
|
+
@bool.must == @bool + @bool
|
249
|
+
@model.solve!
|
250
|
+
end.should_not raise_error
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'should raise error for unsupported right hand sides' do
|
254
|
+
lambda{ @bool.must == 'hello' }.should raise_error(TypeError)
|
255
|
+
end
|
256
|
+
|
257
|
+
it_should_behave_like 'channel constraint between one int and one bool variable'
|
258
|
+
end
|
259
|
+
|
260
|
+
# Requires @model, @bool_enum and @int. Also requires @place_constraint which
|
261
|
+
# is a method that takes four variables: a boolean enum, an integer variable,
|
262
|
+
# whether or not the constraint should be negated and a hash of options, and
|
263
|
+
# places the channel constraint on them.
|
264
|
+
describe 'channel constraint between bool enum and int variable', :shared => true do
|
265
|
+
before do
|
266
|
+
@invoke_options = lambda do |hash|
|
267
|
+
@place_constraint.call(@bools, @int, false, hash)
|
268
|
+
@model.solve!
|
269
|
+
end
|
270
|
+
@expect_options = option_expectation do |strength, kind, reif_var|
|
271
|
+
Gecode::Raw.should_receive(:channel).once.with(
|
272
|
+
an_instance_of(Gecode::Raw::Space),
|
273
|
+
an_instance_of(Gecode::Raw::BoolVarArray),
|
274
|
+
an_instance_of(Gecode::Raw::IntVar), 0,
|
275
|
+
strength, kind)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'should channel the bool enum with the integer variable' do
|
280
|
+
@int.must > 2
|
281
|
+
@place_constraint.call(@bools, @int, false, {})
|
282
|
+
@model.solve!.should_not be_nil
|
283
|
+
int_val = @int.value
|
284
|
+
@bools.values.each_with_index do |bool, index|
|
285
|
+
bool.should == (index == int_val)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'should take the offset into account when channeling' do
|
290
|
+
@int.must > 2
|
291
|
+
offset = 1
|
292
|
+
@place_constraint.call(@bools, @int, false, :offset => offset)
|
293
|
+
@model.solve!.should_not be_nil
|
294
|
+
int_val = @int.value
|
295
|
+
@bools.values.each_with_index do |bool, index|
|
296
|
+
bool.should == (index + offset == int_val)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
it 'should not allow negation' do
|
301
|
+
lambda do
|
302
|
+
@place_constraint.call(@bools, @int, true, {})
|
303
|
+
end.should raise_error(Gecode::MissingConstraintError)
|
304
|
+
end
|
305
|
+
|
306
|
+
it_should_behave_like 'non-reifiable constraint'
|
307
|
+
end
|
308
|
+
|
309
|
+
describe Gecode::Constraints::BoolEnum::Channel, ' (bool enum as lhs with int variable)' do
|
310
|
+
before do
|
311
|
+
@model = BoolChannelSampleProblem.new
|
312
|
+
@bools = @model.bool_enum
|
313
|
+
@int = @model.int
|
314
|
+
|
315
|
+
@place_constraint = lambda do |bools, int, negate, options|
|
316
|
+
unless negate
|
317
|
+
bools.must.channel(int, options)
|
318
|
+
else
|
319
|
+
bools.must_not.channel(int, options)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'should raise error if an integer variable is not given as right hand side' do
|
325
|
+
lambda do
|
326
|
+
@bools.must.channel 'hello'
|
327
|
+
end.should raise_error(TypeError)
|
328
|
+
end
|
329
|
+
|
330
|
+
it_should_behave_like 'channel constraint between bool enum and int variable'
|
331
|
+
end
|
332
|
+
|
333
|
+
|
334
|
+
describe Gecode::Constraints::BoolEnum::Channel, ' (int variable as lhs with bool enum)' do
|
335
|
+
before do
|
336
|
+
@model = BoolChannelSampleProblem.new
|
337
|
+
@bools = @model.bool_enum
|
338
|
+
@int = @model.int
|
339
|
+
|
340
|
+
@place_constraint = lambda do |bools, int, negate, options|
|
341
|
+
unless negate
|
342
|
+
int.must.channel(bools, options)
|
343
|
+
else
|
344
|
+
int.must_not.channel(bools, options)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
it 'should raise error if a boolean enum is not given as right hand side' do
|
350
|
+
lambda do
|
351
|
+
@int.must.channel 'hello'
|
352
|
+
end.should raise_error(TypeError)
|
353
|
+
end
|
354
|
+
|
355
|
+
it_should_behave_like 'channel constraint between bool enum and int variable'
|
356
|
+
end
|
@@ -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
|