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
@@ -0,0 +1,224 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../constraint_helper'
|
2
|
+
|
3
|
+
# Assumes that @variables, @expected_array and @tuples are defined.
|
4
|
+
describe 'int tuple constraint', :shared => true do
|
5
|
+
it 'should not allow negation' do
|
6
|
+
lambda do
|
7
|
+
@variables.must_not_be.in @tuples
|
8
|
+
end.should raise_error(Gecode::MissingConstraintError)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should not allow empty tuples' do
|
12
|
+
lambda do
|
13
|
+
@variables.must_be.in []
|
14
|
+
end.should raise_error(ArgumentError)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should not allow tuples of sizes other than the number of variables' do
|
18
|
+
lambda do
|
19
|
+
@variables.must_be.in([@tuples.first * 2])
|
20
|
+
end.should raise_error(ArgumentError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should raise error if the right hand side does not contain tuples of correct type' do
|
24
|
+
lambda do
|
25
|
+
size = @variables.size
|
26
|
+
@variables.must_be.in ['h'*size, 'i'*size]
|
27
|
+
end.should raise_error(TypeError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe Gecode::IntEnum::Extensional, ' (tuple constraint)' do
|
32
|
+
before do
|
33
|
+
@model = Gecode::Model.new
|
34
|
+
@tuples = [[1,7], [5,1]]
|
35
|
+
@variables = @digits = @model.int_var_array(2, 0..9)
|
36
|
+
@model.branch_on @digits
|
37
|
+
|
38
|
+
@types = [:int_enum]
|
39
|
+
@invoke = lambda do |receiver, hash|
|
40
|
+
receiver.in([[1, 7, 5, 3, 0], [8, 9, 9, 9, 0]], hash)
|
41
|
+
@model.solve!
|
42
|
+
end
|
43
|
+
@expect = lambda do |var, opts, reif_var|
|
44
|
+
Gecode::Raw.should_receive(:extensional).once.with(
|
45
|
+
an_instance_of(Gecode::Raw::Space),
|
46
|
+
var, an_instance_of(Gecode::Raw::TupleSet), *opts)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should constrain the domain of all variables' do
|
51
|
+
@digits.must_be.in @tuples
|
52
|
+
|
53
|
+
found_solutions = []
|
54
|
+
@model.each_solution do |m|
|
55
|
+
found_solutions << @digits.values
|
56
|
+
end
|
57
|
+
|
58
|
+
found_solutions.size.should == 2
|
59
|
+
(found_solutions - @tuples).should be_empty
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should raise error if the right hand side is not an enumeration' do
|
63
|
+
lambda{ @digits.must_be.in 4711 }.should raise_error(TypeError)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should raise error if the right hand side does not contain tuples' do
|
67
|
+
lambda{ @digits.must_be.in [17, 4711] }.should raise_error(TypeError)
|
68
|
+
end
|
69
|
+
|
70
|
+
it_should_behave_like 'int tuple constraint'
|
71
|
+
it_should_behave_like 'non-reifiable constraint'
|
72
|
+
end
|
73
|
+
|
74
|
+
# Assumes that @variables, @expected_array, @value1, @value2 (must not
|
75
|
+
# equal @value1) and @regexp are defined.
|
76
|
+
describe 'int regular expression constraint', :shared => true do
|
77
|
+
it 'should handle values grouped in a single array' do
|
78
|
+
@variables.must.match [@value1, @value2, @value1]
|
79
|
+
@model.solve!.should_not be_nil
|
80
|
+
@variables.values.should == [@value1, @value2, @value1]
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should allow nested groups of values' do
|
84
|
+
@variables.must.match [@value1, [@value2, [@value1]]]
|
85
|
+
@model.solve!.should_not be_nil
|
86
|
+
@variables.values.should == [@value1, @value2, @value1]
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should handle the repeat operation' do
|
90
|
+
@variables.must.match [@value1, @model.repeat([@value2], 1, 2)]
|
91
|
+
@model.solve!.should_not be_nil
|
92
|
+
@variables.values.should == [@value1, @value2, @value2]
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should handle repeat operations that do not encase constant values in arrays' do
|
96
|
+
@variables.must.match [@value1, @model.repeat(@value2, 1, 2)]
|
97
|
+
@model.solve!.should_not be_nil
|
98
|
+
@variables.values.should == [@value1, @value2, @value2]
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should handle nested repeat operations' do
|
102
|
+
@variables.must.match [[@model.repeat(@model.repeat([@value2], 1, 3), 1, 2)]]
|
103
|
+
@model.solve!.should_not be_nil
|
104
|
+
@variables.values.should == [@value2, @value2, @value2]
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should handle nested repeat operations (2)' do
|
108
|
+
@variables.must.match [[@model.repeat([@model.repeat(@value2, 1, 3)], 1, 2)]]
|
109
|
+
@model.solve!.should_not be_nil
|
110
|
+
@variables.values.should == [@value2, @value2, @value2]
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should interpret the repeat operation with the last argument omitted as only giving a lower bound' do
|
114
|
+
@variables.must.match [@value1, @model.repeat([@value2], 1)]
|
115
|
+
@model.solve!.should_not be_nil
|
116
|
+
@variables.values.should == [@value1, @value2, @value2]
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should interpret the repeat operation with all but the first argument omitted as not giving any bound' do
|
120
|
+
@variables.must.match [@model.repeat(@value2), @value1, @value1, @value1]
|
121
|
+
@model.solve!.should_not be_nil
|
122
|
+
@variables.values.should == [@value1, @value1, @value1]
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should interpret the repeat operation with all but the first argument omitted as not giving any bound (2)' do
|
126
|
+
@variables.must.match [@model.repeat(@value2)]
|
127
|
+
@model.solve!.should_not be_nil
|
128
|
+
@variables.values.should == [@value2, @value2, @value2]
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should translate at_most_once(reg) to repeat(reg, 0, 1)' do
|
132
|
+
@model.should_receive(:repeat).once.with([@value1], 0, 1)
|
133
|
+
@model.at_most_once [@value1]
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should translate at_least_once(reg) to repeat(reg, 1)' do
|
137
|
+
@model.should_receive(:repeat).once.with([@value1], 1)
|
138
|
+
@model.at_least_once [@value1]
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should raise error if the right hand side is not an enumeration' do
|
142
|
+
lambda do
|
143
|
+
@variables.must.match Object.new
|
144
|
+
end.should raise_error(TypeError)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should raise error if the right hand side does not a regexp of the right type' do
|
148
|
+
lambda do
|
149
|
+
@variables.must.match [@value1, 'foo']
|
150
|
+
end.should raise_error(TypeError)
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should raise error if the right hand side contains a nested element of an incorrect type' do
|
154
|
+
lambda do
|
155
|
+
@variables.must.match [@value1, [@value2, 'foo']]
|
156
|
+
end.should raise_error(TypeError)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should raise error if the repeat operation is given arguments of incorrect type (2)' do
|
160
|
+
lambda do
|
161
|
+
@variables.must.match @model.repeat(@value1, [0], 1)
|
162
|
+
end.should raise_error(TypeError)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should raise error if the repeat operation is given arguments of incorrect type (3)' do
|
166
|
+
lambda do
|
167
|
+
@variables.must.match @model.repeat(@value1, 0, [1])
|
168
|
+
end.should raise_error(TypeError)
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'should raise error if the repeat operation is given arguments of incorrect type' do
|
172
|
+
lambda do
|
173
|
+
@variables.must.match @model.repeat('foo', 0, 1)
|
174
|
+
end.should raise_error(TypeError)
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'should not allow negation' do
|
178
|
+
lambda do
|
179
|
+
@variables.must_not.match @regexp
|
180
|
+
end.should raise_error(Gecode::MissingConstraintError)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe Gecode::IntEnum::Extensional, ' (regexp constraint)' do
|
185
|
+
before do
|
186
|
+
@model = Gecode::Model.new
|
187
|
+
@variables = @digits = @model.int_var_array(3, 0..9)
|
188
|
+
@model.branch_on @digits
|
189
|
+
@expected_array = an_instance_of Gecode::Raw::IntVarArray
|
190
|
+
@value1 = 3
|
191
|
+
@value2 = 5
|
192
|
+
@regexp = [1, @model.any(3, 4), @model.at_most_once(5)]
|
193
|
+
|
194
|
+
@types = [:int_enum]
|
195
|
+
@invoke = lambda do |receiver, hash|
|
196
|
+
receiver.match(@regexp, hash)
|
197
|
+
@model.solve!
|
198
|
+
end
|
199
|
+
@expect = lambda do |var, opts, reif_var|
|
200
|
+
Gecode::Raw.should_receive(:extensional).once.with(
|
201
|
+
an_instance_of(Gecode::Raw::Space),
|
202
|
+
var, an_instance_of(Gecode::Raw::REG), *opts)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should handle the any operation' do
|
207
|
+
@digits.must.match [1, @model.any(1, 2, 3), 3]
|
208
|
+
@model.solve!.should_not be_nil
|
209
|
+
values = @digits.values
|
210
|
+
values.size.should == 3
|
211
|
+
values.should == values.sort
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'should handle the any operator with nested expressions' do
|
215
|
+
@digits.must.match [1, @model.any(@model.at_least_once(2), [3, 5])]
|
216
|
+
@digits[2].must < 4
|
217
|
+
@model.solve!.should_not be_nil
|
218
|
+
@digits.values.should == [1,2,2]
|
219
|
+
end
|
220
|
+
|
221
|
+
it_should_behave_like 'int regular expression constraint'
|
222
|
+
it_should_behave_like 'non-reifiable constraint'
|
223
|
+
end
|
224
|
+
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../constraint_helper'
|
2
|
+
|
3
|
+
class SortSampleProblem < Gecode::Model
|
4
|
+
attr :vars
|
5
|
+
attr :sorted
|
6
|
+
attr :indices
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@vars = int_var_array(4, 10..19)
|
10
|
+
@sorted = int_var_array(4, 10..19)
|
11
|
+
@indices = int_var_array(4, 0..9)
|
12
|
+
|
13
|
+
# To make it more interesting
|
14
|
+
@vars.must_be.distinct
|
15
|
+
|
16
|
+
branch_on @vars
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe Gecode::IntEnum::Sort, ' (without :as and :order)' do
|
21
|
+
before do
|
22
|
+
@model = SortSampleProblem.new
|
23
|
+
@vars = @model.vars
|
24
|
+
@sorted = @model.sorted
|
25
|
+
|
26
|
+
@types = [:int_enum]
|
27
|
+
@invoke = lambda do |receiver, hash|
|
28
|
+
receiver.sorted(hash)
|
29
|
+
@model.solve!
|
30
|
+
end
|
31
|
+
@expect = lambda do |var, opts, reif_var|
|
32
|
+
if reif_var.nil?
|
33
|
+
Gecode::Raw.should_receive(:rel).exactly(var.size - 1).times.with(
|
34
|
+
an_instance_of(Gecode::Raw::Space),
|
35
|
+
an_instance_of(Gecode::Raw::IntVar), Gecode::Raw::IRT_LQ,
|
36
|
+
an_instance_of(Gecode::Raw::IntVar), *opts)
|
37
|
+
else
|
38
|
+
Gecode::Raw.should_receive(:rel).once.with(
|
39
|
+
an_instance_of(Gecode::Raw::Space), anything,
|
40
|
+
an_instance_of(Gecode::Raw::BoolVarArray),
|
41
|
+
anything, anything, anything)
|
42
|
+
Gecode::Raw.should_receive(:rel).exactly(var.size - 1).times.with(
|
43
|
+
an_instance_of(Gecode::Raw::Space),
|
44
|
+
an_instance_of(Gecode::Raw::IntVar), Gecode::Raw::IRT_LQ,
|
45
|
+
an_instance_of(Gecode::Raw::IntVar),
|
46
|
+
an_instance_of(Gecode::Raw::BoolVar), *opts)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should constraint variables to be sorted' do
|
52
|
+
@vars.must_be.sorted
|
53
|
+
values = @model.solve!.vars.values
|
54
|
+
values.should == values.sort
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should allow negation' do
|
58
|
+
@vars.must_not_be.sorted
|
59
|
+
@model.solve!
|
60
|
+
values = @vars.values
|
61
|
+
values.should_not == values.sort
|
62
|
+
end
|
63
|
+
|
64
|
+
it_should_behave_like 'reifiable constraint'
|
65
|
+
end
|
66
|
+
|
67
|
+
describe Gecode::IntEnum::Sort, ' (with :as)' do
|
68
|
+
before do
|
69
|
+
@model = SortSampleProblem.new
|
70
|
+
@vars = @model.vars
|
71
|
+
@sorted = @model.sorted
|
72
|
+
|
73
|
+
# Make it a bit more interesting.
|
74
|
+
@vars[0].must > @vars[3] + 1
|
75
|
+
|
76
|
+
@types = [:int_enum, :int_enum]
|
77
|
+
@invoke = lambda do |receiver, int_enum, hash|
|
78
|
+
receiver.sorted hash.update(:as => int_enum)
|
79
|
+
@model.solve!
|
80
|
+
end
|
81
|
+
@expect = lambda do |var1, var2, opts, reif_var|
|
82
|
+
Gecode::Raw.should_receive(:sorted).once.with(
|
83
|
+
an_instance_of(Gecode::Raw::Space),
|
84
|
+
var1, var2, *opts)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should constraint variables to be sorted' do
|
89
|
+
@vars.must_be.sorted(:as => @sorted)
|
90
|
+
@model.solve!
|
91
|
+
values = @sorted.values
|
92
|
+
values.should == values.sort
|
93
|
+
end
|
94
|
+
|
95
|
+
it_should_behave_like 'non-reifiable constraint'
|
96
|
+
it_should_behave_like 'non-negatable constraint'
|
97
|
+
end
|
98
|
+
|
99
|
+
describe Gecode::IntEnum::Sort, ' (with :order)' do
|
100
|
+
before do
|
101
|
+
@model = SortSampleProblem.new
|
102
|
+
@vars = @model.vars
|
103
|
+
@sorted = @model.sorted
|
104
|
+
@indices = @model.indices
|
105
|
+
|
106
|
+
# Make it a bit more interesting.
|
107
|
+
@vars[0].must > @vars[3] + 1
|
108
|
+
|
109
|
+
@types = [:int_enum, :int_enum]
|
110
|
+
@invoke = lambda do |receiver, int_enum, hash|
|
111
|
+
receiver.sorted hash.update(:order => int_enum)
|
112
|
+
@model.solve!
|
113
|
+
end
|
114
|
+
@expect = lambda do |var1, var2, opts, reif_var|
|
115
|
+
Gecode::Raw.should_receive(:sorted).once.with(
|
116
|
+
an_instance_of(Gecode::Raw::Space),
|
117
|
+
var1, an_instance_of(Gecode::Raw::IntVarArray), var2,
|
118
|
+
*opts)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should constraint variables to be sorted with the specified indices' do
|
123
|
+
@vars.must_be.sorted(:order => @indices)
|
124
|
+
@model.solve!
|
125
|
+
sorted_values = @vars.values.sort
|
126
|
+
expected_indices = @vars.map{ |v| sorted_values.index(v.value) }
|
127
|
+
@indices.values.should == expected_indices
|
128
|
+
end
|
129
|
+
|
130
|
+
it_should_behave_like 'non-reifiable constraint'
|
131
|
+
it_should_behave_like 'non-negatable constraint'
|
132
|
+
end
|
133
|
+
|
134
|
+
describe Gecode::IntEnum::Sort, ' (with :order and :as)' do
|
135
|
+
before do
|
136
|
+
@model = SortSampleProblem.new
|
137
|
+
@vars = @model.vars
|
138
|
+
@sorted = @model.sorted
|
139
|
+
@indices = @model.indices
|
140
|
+
|
141
|
+
# Make it a bit more interesting.
|
142
|
+
@vars[0].must > @vars[3] + 1
|
143
|
+
|
144
|
+
@types = [:int_enum, :int_enum, :int_enum]
|
145
|
+
@invoke = lambda do |receiver, int_enum1, int_enum2, hash|
|
146
|
+
receiver.sorted hash.update(:as => int_enum1, :order => int_enum2)
|
147
|
+
@model.solve!
|
148
|
+
end
|
149
|
+
@expect = lambda do |var1, var2, var3, opts, reif_var|
|
150
|
+
Gecode::Raw.should_receive(:sorted).once.with(
|
151
|
+
an_instance_of(Gecode::Raw::Space),
|
152
|
+
var1, var2, var3, *opts)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'should constraint variables to be sorted with the specified indices' do
|
157
|
+
@vars.must_be.sorted(:as => @sorted, :order => @indices)
|
158
|
+
@model.solve!
|
159
|
+
sorted_values = @sorted.values
|
160
|
+
sorted_values.should == sorted_values.sort
|
161
|
+
expected_indices = @vars.map{ |v| sorted_values.index(v.value) }
|
162
|
+
@indices.values.should == expected_indices
|
163
|
+
end
|
164
|
+
|
165
|
+
it_should_behave_like 'non-reifiable constraint'
|
166
|
+
it_should_behave_like 'non-negatable constraint'
|
167
|
+
end
|
@@ -0,0 +1,264 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe 'operand', :shared => true do
|
4
|
+
it 'should not shadow method missing' do
|
5
|
+
lambda do
|
6
|
+
@operand.does_not_exist
|
7
|
+
end.should raise_error(NoMethodError)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'variable operand', :shared => true do
|
12
|
+
it 'should fall back to the underlying variable' do
|
13
|
+
lambda do
|
14
|
+
@operand.assigned?
|
15
|
+
end.should_not raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
it_should_behave_like 'operand'
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'enum operand', :shared => true do
|
22
|
+
it 'should fall back to the underlying enum' do
|
23
|
+
lambda do
|
24
|
+
@operand[0]
|
25
|
+
end.should_not raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
it_should_behave_like 'operand'
|
29
|
+
end
|
30
|
+
|
31
|
+
describe Gecode::Operand do
|
32
|
+
before do
|
33
|
+
model = Gecode::Model.new
|
34
|
+
@operand = Object.new
|
35
|
+
class <<@operand
|
36
|
+
include Gecode::Operand
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should raise error if #model is not defined' do
|
41
|
+
lambda do
|
42
|
+
@operand.model
|
43
|
+
end.should raise_error(NotImplementedError)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should raise error if #construct_receiver is not defined' do
|
47
|
+
lambda do
|
48
|
+
@operand.must
|
49
|
+
end.should raise_error(NotImplementedError)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe Gecode::Int::IntOperand do
|
54
|
+
before do
|
55
|
+
model = Gecode::Model.new
|
56
|
+
@operand, _ = general_int_operand(model)
|
57
|
+
end
|
58
|
+
|
59
|
+
it_should_behave_like 'variable operand'
|
60
|
+
end
|
61
|
+
|
62
|
+
describe Gecode::Int::ShortCircuitEqualityOperand, ' (not subclassed)' do
|
63
|
+
before do
|
64
|
+
@model = Gecode::Model.new
|
65
|
+
@operand = Gecode::Int::ShortCircuitEqualityOperand.new(@model)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should raise error if #constrain_equal is called' do
|
69
|
+
lambda do
|
70
|
+
@operand.to_int_var
|
71
|
+
@model.solve!
|
72
|
+
end.should raise_error(NotImplementedError)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe Gecode::Int::ShortCircuitRelationsOperand, ' (not subclassed)' do
|
77
|
+
before do
|
78
|
+
@model = Gecode::Model.new
|
79
|
+
@operand = Gecode::Int::ShortCircuitRelationsOperand.new(@model)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should raise error if #constrain_equal is called' do
|
83
|
+
lambda do
|
84
|
+
@operand.to_int_var
|
85
|
+
@model.solve!
|
86
|
+
end.should raise_error(NotImplementedError)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe Gecode::IntEnum::IntEnumOperand do
|
91
|
+
before do
|
92
|
+
model = Gecode::Model.new
|
93
|
+
@operand, _ = general_int_enum_operand(model)
|
94
|
+
end
|
95
|
+
|
96
|
+
it_should_behave_like 'enum operand'
|
97
|
+
end
|
98
|
+
|
99
|
+
describe Gecode::Bool::BoolOperand do
|
100
|
+
before do
|
101
|
+
model = Gecode::Model.new
|
102
|
+
@operand, _ = general_bool_operand(model)
|
103
|
+
end
|
104
|
+
|
105
|
+
it_should_behave_like 'variable operand'
|
106
|
+
end
|
107
|
+
|
108
|
+
describe Gecode::Bool::ShortCircuitEqualityOperand, ' (not subclassed)' do
|
109
|
+
before do
|
110
|
+
@model = Gecode::Model.new
|
111
|
+
@operand = Gecode::Bool::ShortCircuitEqualityOperand.new(@model)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should raise error if #constrain_equal is called' do
|
115
|
+
lambda do
|
116
|
+
@operand.to_bool_var
|
117
|
+
@model.solve!
|
118
|
+
end.should raise_error(NotImplementedError)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe Gecode::BoolEnum::BoolEnumOperand do
|
123
|
+
before do
|
124
|
+
model = Gecode::Model.new
|
125
|
+
@operand, _ = general_bool_enum_operand(model)
|
126
|
+
end
|
127
|
+
|
128
|
+
it_should_behave_like 'enum operand'
|
129
|
+
end
|
130
|
+
|
131
|
+
describe Gecode::Set::SetOperand do
|
132
|
+
before do
|
133
|
+
model = Gecode::Model.new
|
134
|
+
@operand, _ = general_set_operand(model)
|
135
|
+
end
|
136
|
+
|
137
|
+
it_should_behave_like 'variable operand'
|
138
|
+
end
|
139
|
+
|
140
|
+
describe Gecode::Set::ShortCircuitEqualityOperand, ' (not subclassed)' do
|
141
|
+
before do
|
142
|
+
@model = Gecode::Model.new
|
143
|
+
@operand = Gecode::Set::ShortCircuitEqualityOperand.new(@model)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should raise error if #constrain_equal is called' do
|
147
|
+
lambda do
|
148
|
+
@operand.to_set_var
|
149
|
+
@model.solve!
|
150
|
+
end.should raise_error(NotImplementedError)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe Gecode::Set::ShortCircuitRelationsOperand, ' (not subclassed)' do
|
155
|
+
before do
|
156
|
+
@model = Gecode::Model.new
|
157
|
+
@operand = Gecode::Set::ShortCircuitRelationsOperand.new(@model)
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'should raise error if #constrain_equal is called' do
|
161
|
+
lambda do
|
162
|
+
@operand.to_set_var
|
163
|
+
@model.solve!
|
164
|
+
end.should raise_error(NotImplementedError)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe Gecode::SelectedSet::SelectedSetOperand do
|
169
|
+
before do
|
170
|
+
model = Gecode::Model.new
|
171
|
+
@operand, ops = general_selected_set_operand(model)
|
172
|
+
@enum, @set = ops
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'should raise error if set enum operand is not given' do
|
176
|
+
lambda do
|
177
|
+
Gecode::SelectedSet::SelectedSetOperand.new(:foo, @set)
|
178
|
+
end.should raise_error(TypeError)
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should raise error if set operand is not given' do
|
182
|
+
lambda do
|
183
|
+
Gecode::SelectedSet::SelectedSetOperand.new(@enum, :foo)
|
184
|
+
end.should raise_error(TypeError)
|
185
|
+
end
|
186
|
+
|
187
|
+
it_should_behave_like 'operand'
|
188
|
+
end
|
189
|
+
|
190
|
+
describe Gecode::SetElements::SetElementsOperand do
|
191
|
+
before do
|
192
|
+
model = Gecode::Model.new
|
193
|
+
@operand, @set = general_set_elements_operand(model)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'should raise error if set operand is not given' do
|
197
|
+
lambda do
|
198
|
+
Gecode::SetElements::SetElementsOperand.new(:foo)
|
199
|
+
end.should raise_error(TypeError)
|
200
|
+
end
|
201
|
+
|
202
|
+
it_should_behave_like 'operand'
|
203
|
+
end
|
204
|
+
|
205
|
+
describe Gecode::SetEnum::SetEnumOperand do
|
206
|
+
before do
|
207
|
+
model = Gecode::Model.new
|
208
|
+
@operand, _ = general_set_enum_operand(model)
|
209
|
+
end
|
210
|
+
|
211
|
+
it_should_behave_like 'enum operand'
|
212
|
+
end
|
213
|
+
|
214
|
+
describe Gecode::FixnumEnum::FixnumEnumOperand do
|
215
|
+
before do
|
216
|
+
model = Gecode::Model.new
|
217
|
+
@operand, _ = general_fixnum_enum_operand(model)
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'should raise error if constraint receiver is called' do
|
221
|
+
lambda do
|
222
|
+
@operand.must
|
223
|
+
end.should raise_error(NotImplementedError)
|
224
|
+
end
|
225
|
+
|
226
|
+
it_should_behave_like 'enum operand'
|
227
|
+
end
|
228
|
+
|
229
|
+
# High level sanity check.
|
230
|
+
describe 'operand combination' do
|
231
|
+
it 'should allow placing constraints on complicated combinations of operands' do
|
232
|
+
model = Gecode::Model.new
|
233
|
+
sets = model.set_var_array(2, [], 0..9)
|
234
|
+
set1, set2 = sets
|
235
|
+
(set1.size + set2.size).must == 5
|
236
|
+
set1.size.must > 1
|
237
|
+
set2.size.must > 1
|
238
|
+
model.branch_on sets
|
239
|
+
|
240
|
+
model.solve!
|
241
|
+
|
242
|
+
(set1.value.size + set2.value.size).should == 5
|
243
|
+
set1.value.size.should > 1
|
244
|
+
set2.value.size.should > 1
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'should allow placing constraints on complicated combinations of operands (2)' do
|
248
|
+
model = Gecode::Model.new
|
249
|
+
sets = model.set_var_array(3, [], 0..9)
|
250
|
+
set1, set2, set3 = sets
|
251
|
+
(set1.size + set2.size).must == (set3.size - set1.max)
|
252
|
+
set3.size.must > 3
|
253
|
+
set2.size.must < 3
|
254
|
+
set1.size.must > 0
|
255
|
+
model.branch_on sets
|
256
|
+
|
257
|
+
model.solve!
|
258
|
+
|
259
|
+
(set1.value.size + set2.value.size).should == (set3.value.size - set1.value.max)
|
260
|
+
set3.value.size.should > 3
|
261
|
+
set2.value.size.should < 3
|
262
|
+
set1.value.size.should > 0
|
263
|
+
end
|
264
|
+
end
|