gecoder 0.3.0 → 0.4.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 +17 -2
- data/README +7 -1
- data/Rakefile +4 -0
- data/lib/gecoder/interface/constraints/bool/boolean.rb +1 -4
- data/lib/gecoder/interface/constraints/int/arithmetic.rb +77 -0
- data/lib/gecoder/interface/constraints/int/domain.rb +50 -0
- data/lib/gecoder/interface/constraints/int/linear.rb +12 -44
- data/lib/gecoder/interface/constraints/int_enum/arithmetic.rb +72 -0
- data/lib/gecoder/interface/constraints/int_enum/channel.rb +32 -0
- data/lib/gecoder/interface/constraints/int_enum/count.rb +90 -0
- data/lib/gecoder/interface/constraints/int_enum/distinct.rb +3 -8
- data/lib/gecoder/interface/constraints/int_enum/element.rb +75 -0
- data/lib/gecoder/interface/constraints/int_enum/equality.rb +31 -0
- data/lib/gecoder/interface/constraints/int_enum/sort.rb +104 -0
- data/lib/gecoder/interface/constraints/int_enum_constraints.rb +6 -0
- data/lib/gecoder/interface/constraints/int_var_constraints.rb +2 -0
- data/lib/gecoder/interface/constraints/reifiable_constraints.rb +21 -0
- data/lib/gecoder/interface/constraints.rb +57 -6
- data/lib/gecoder/interface/enum_matrix.rb +64 -0
- data/lib/gecoder/interface/enum_wrapper.rb +33 -5
- data/lib/gecoder/interface/model.rb +36 -6
- data/lib/gecoder/interface.rb +1 -0
- data/lib/gecoder/version.rb +4 -0
- data/lib/gecoder.rb +1 -0
- data/specs/binding_changes.rb +72 -0
- data/specs/bool_var.rb +20 -0
- data/specs/branch.rb +104 -0
- data/specs/constraints/arithmetic.rb +227 -0
- data/specs/constraints/boolean.rb +132 -0
- data/specs/constraints/channel.rb +55 -0
- data/specs/constraints/constraint_helper.rb +48 -0
- data/specs/constraints/constraints.rb +28 -0
- data/specs/constraints/count.rb +99 -0
- data/specs/constraints/distinct.rb +99 -0
- data/specs/constraints/domain.rb +56 -0
- data/specs/constraints/element.rb +128 -0
- data/specs/constraints/equality.rb +30 -0
- data/specs/constraints/linear.rb +166 -0
- data/specs/constraints/reification_sugar.rb +92 -0
- data/specs/constraints/relation.rb +72 -0
- data/specs/constraints/sort.rb +173 -0
- data/specs/enum_matrix.rb +43 -0
- data/specs/enum_wrapper.rb +100 -0
- data/specs/int_var.rb +108 -0
- data/specs/model.rb +84 -0
- data/specs/search.rb +157 -0
- data/specs/spec_helper.rb +63 -0
- data/specs/tmp +135 -0
- data/tasks/all_tasks.rb +1 -0
- data/tasks/distribution.rake +64 -0
- data/tasks/rcov.rake +17 -0
- data/tasks/specs.rake +16 -0
- data/tasks/svn.rake +11 -0
- data/tasks/website.rake +58 -0
- data/vendor/rust/include/rust_conversions.hh +1 -2
- metadata +53 -2
@@ -0,0 +1,227 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/constraint_helper'
|
3
|
+
|
4
|
+
class ArithmeticSampleProblem < Gecode::Model
|
5
|
+
attr :numbers
|
6
|
+
attr :var
|
7
|
+
attr :var2
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@numbers = int_var_array(4, 0..9)
|
11
|
+
@var = int_var(-9..9)
|
12
|
+
@var2 = int_var(0..9)
|
13
|
+
branch_on @numbers
|
14
|
+
branch_on wrap_enum([@var, @var2])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'arithmetic constraint', :shared => true do
|
19
|
+
situations = {
|
20
|
+
'variable bound' => nil,
|
21
|
+
'constant bound' => 5
|
22
|
+
}.each_pair do |description, bound|
|
23
|
+
Gecode::Constraints::Util::RELATION_TYPES.each_pair do |relation, type|
|
24
|
+
it "should translate #{relation} with #{description}" do
|
25
|
+
bound = @var if bound.nil?
|
26
|
+
@expect.call(type, bound, Gecode::Raw::ICL_DEF, nil)
|
27
|
+
@stub.must.send(relation, bound)
|
28
|
+
@model.solve!
|
29
|
+
end
|
30
|
+
end
|
31
|
+
Gecode::Constraints::Util::NEGATED_RELATION_TYPES.each_pair do |relation, type|
|
32
|
+
it "should translate negated #{relation} with #{description}" do
|
33
|
+
bound = @var if bound.nil?
|
34
|
+
@expect.call(type, bound, Gecode::Raw::ICL_DEF, nil)
|
35
|
+
@stub.must_not.send(relation, bound)
|
36
|
+
@model.solve!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should raise error if the right hand side is of the wrong type' do
|
42
|
+
lambda{ @stub.must == 'hello' }.should raise_error(TypeError)
|
43
|
+
end
|
44
|
+
|
45
|
+
it_should_behave_like 'constraint with options'
|
46
|
+
end
|
47
|
+
|
48
|
+
describe Gecode::Constraints::IntEnum::Arithmetic, ' (max)' do
|
49
|
+
before do
|
50
|
+
@model = ArithmeticSampleProblem.new
|
51
|
+
@numbers = @model.numbers
|
52
|
+
@var = @model.var
|
53
|
+
@stub = @numbers.max
|
54
|
+
|
55
|
+
# Creates an expectation corresponding to the specified input.
|
56
|
+
@expect = lambda do |relation, rhs, strength, reif_var|
|
57
|
+
rhs = rhs.bind if rhs.respond_to? :bind
|
58
|
+
if reif_var.nil?
|
59
|
+
Gecode::Raw.should_receive(:max).once.with(@model.active_space,
|
60
|
+
an_instance_of(Gecode::Raw::IntVarArray),
|
61
|
+
an_instance_of(Gecode::Raw::IntVar), an_instance_of(Fixnum))
|
62
|
+
Gecode::Raw.should_receive(:rel).once.with(@model.active_space,
|
63
|
+
an_instance_of(Gecode::Raw::IntVar), relation, rhs, strength)
|
64
|
+
else
|
65
|
+
Gecode::Raw.should_receive(:max).once.with(@model.active_space,
|
66
|
+
an_instance_of(Gecode::Raw::IntVarArray),
|
67
|
+
an_instance_of(Gecode::Raw::IntVar), an_instance_of(Fixnum))
|
68
|
+
Gecode::Raw.should_receive(:rel).once.with(@model.active_space,
|
69
|
+
an_instance_of(Gecode::Raw::IntVar), relation, rhs, reif_var.bind,
|
70
|
+
strength)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# For constraint option spec.
|
75
|
+
@invoke_options = lambda do |hash|
|
76
|
+
@numbers.max.must_be.greater_than(@var, hash)
|
77
|
+
@model.solve!
|
78
|
+
end
|
79
|
+
@expect_options = lambda do |strength, reif_var|
|
80
|
+
@expect.call(Gecode::Raw::IRT_GR, @var, strength, reif_var)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should constrain the maximum value' do
|
85
|
+
@numbers.max.must > 5
|
86
|
+
@model.solve!.numbers.map{ |n| n.val }.max.should > 5
|
87
|
+
end
|
88
|
+
|
89
|
+
it_should_behave_like 'arithmetic constraint'
|
90
|
+
end
|
91
|
+
|
92
|
+
describe Gecode::Constraints::IntEnum::Arithmetic, ' (min)' do
|
93
|
+
before do
|
94
|
+
@model = ArithmeticSampleProblem.new
|
95
|
+
@numbers = @model.numbers
|
96
|
+
@var = @model.var
|
97
|
+
@stub = @numbers.min
|
98
|
+
|
99
|
+
# Creates an expectation corresponding to the specified input.
|
100
|
+
@expect = lambda do |relation, rhs, strength, reif_var|
|
101
|
+
rhs = rhs.bind if rhs.respond_to? :bind
|
102
|
+
if reif_var.nil?
|
103
|
+
Gecode::Raw.should_receive(:min).once.with(@model.active_space,
|
104
|
+
an_instance_of(Gecode::Raw::IntVarArray),
|
105
|
+
an_instance_of(Gecode::Raw::IntVar), an_instance_of(Fixnum))
|
106
|
+
Gecode::Raw.should_receive(:rel).once.with(@model.active_space,
|
107
|
+
an_instance_of(Gecode::Raw::IntVar), relation, rhs, strength)
|
108
|
+
else
|
109
|
+
Gecode::Raw.should_receive(:min).once.with(@model.active_space,
|
110
|
+
an_instance_of(Gecode::Raw::IntVarArray),
|
111
|
+
an_instance_of(Gecode::Raw::IntVar), an_instance_of(Fixnum))
|
112
|
+
Gecode::Raw.should_receive(:rel).once.with(@model.active_space,
|
113
|
+
an_instance_of(Gecode::Raw::IntVar), relation, rhs, reif_var.bind,
|
114
|
+
strength)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# For constraint option spec.
|
119
|
+
@invoke_options = lambda do |hash|
|
120
|
+
@numbers.min.must_be.greater_than(@var, hash)
|
121
|
+
@model.solve!
|
122
|
+
end
|
123
|
+
@expect_options = lambda do |strength, reif_var|
|
124
|
+
@expect.call(Gecode::Raw::IRT_GR, @var, strength, reif_var)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should constrain the minimum value' do
|
129
|
+
@numbers.min.must > 5
|
130
|
+
@model.solve!.numbers.map{ |n| n.val }.min.should > 5
|
131
|
+
end
|
132
|
+
|
133
|
+
it_should_behave_like 'arithmetic constraint'
|
134
|
+
end
|
135
|
+
|
136
|
+
describe Gecode::Constraints::Int::Arithmetic, ' (abs)' do
|
137
|
+
before do
|
138
|
+
@model = ArithmeticSampleProblem.new
|
139
|
+
@var = @model.var
|
140
|
+
@stub = @var.abs
|
141
|
+
|
142
|
+
# Creates an expectation corresponding to the specified input.
|
143
|
+
@expect = lambda do |relation, rhs, strength, reif_var|
|
144
|
+
rhs = rhs.bind if rhs.respond_to? :bind
|
145
|
+
if reif_var.nil?
|
146
|
+
Gecode::Raw.should_receive(:abs).once.with(@model.active_space,
|
147
|
+
@var.bind, an_instance_of(Gecode::Raw::IntVar),
|
148
|
+
an_instance_of(Fixnum))
|
149
|
+
Gecode::Raw.should_receive(:rel).once.with(@model.active_space,
|
150
|
+
an_instance_of(Gecode::Raw::IntVar), relation, rhs, strength)
|
151
|
+
else
|
152
|
+
Gecode::Raw.should_receive(:abs).once.with(@model.active_space,
|
153
|
+
@var.bind, an_instance_of(Gecode::Raw::IntVar),
|
154
|
+
an_instance_of(Fixnum))
|
155
|
+
Gecode::Raw.should_receive(:rel).once.with(@model.active_space,
|
156
|
+
an_instance_of(Gecode::Raw::IntVar), relation, rhs, reif_var.bind,
|
157
|
+
strength)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# For constraint option spec.
|
162
|
+
@invoke_options = lambda do |hash|
|
163
|
+
@var.abs.must_be.greater_than(@var, hash)
|
164
|
+
@model.solve!
|
165
|
+
end
|
166
|
+
@expect_options = lambda do |strength, reif_var|
|
167
|
+
@expect.call(Gecode::Raw::IRT_GR, @var, strength, reif_var)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'should constrain the absolute value' do
|
172
|
+
@var.must < 0
|
173
|
+
@var.abs.must == 5
|
174
|
+
@model.solve!.var.val.should == -5
|
175
|
+
end
|
176
|
+
|
177
|
+
it_should_behave_like 'arithmetic constraint'
|
178
|
+
end
|
179
|
+
|
180
|
+
describe Gecode::Constraints::Int::Arithmetic, ' (multiplication)' do
|
181
|
+
before do
|
182
|
+
@model = ArithmeticSampleProblem.new
|
183
|
+
@var = @model.var
|
184
|
+
@var2 = @model.var2
|
185
|
+
@stub = @var * @var2
|
186
|
+
|
187
|
+
# Creates an expectation corresponding to the specified input.
|
188
|
+
@expect = lambda do |relation, rhs, strength, reif_var|
|
189
|
+
rhs = rhs.bind if rhs.respond_to? :bind
|
190
|
+
if reif_var.nil?
|
191
|
+
Gecode::Raw.should_receive(:mult).once.with(@model.active_space,
|
192
|
+
@var.bind, @var2.bind, an_instance_of(Gecode::Raw::IntVar),
|
193
|
+
an_instance_of(Fixnum))
|
194
|
+
Gecode::Raw.should_receive(:rel).once.with(@model.active_space,
|
195
|
+
an_instance_of(Gecode::Raw::IntVar), relation, rhs, strength)
|
196
|
+
else
|
197
|
+
Gecode::Raw.should_receive(:mult).once.with(@model.active_space,
|
198
|
+
@var.bind, @var2.bind, an_instance_of(Gecode::Raw::IntVar),
|
199
|
+
an_instance_of(Fixnum))
|
200
|
+
Gecode::Raw.should_receive(:rel).once.with(@model.active_space,
|
201
|
+
an_instance_of(Gecode::Raw::IntVar), relation, rhs, reif_var.bind,
|
202
|
+
strength)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# For constraint option spec.
|
207
|
+
@invoke_options = lambda do |hash|
|
208
|
+
(@var * @var2).must_be.greater_than(@var, hash)
|
209
|
+
@model.solve!
|
210
|
+
end
|
211
|
+
@expect_options = lambda do |strength, reif_var|
|
212
|
+
@expect.call(Gecode::Raw::IRT_GR, @var, strength, reif_var)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'should constrain the value of the multiplication' do
|
217
|
+
(@var * @var2).must == 56
|
218
|
+
sol = @model.solve!
|
219
|
+
[sol.var.val, sol.var2.val].sort.should == [7, 8]
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'should not interfere with other defined multiplication methods' do
|
223
|
+
(@var * :foo).should be_nil
|
224
|
+
end
|
225
|
+
|
226
|
+
it_should_behave_like 'arithmetic constraint'
|
227
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
class BoolSampleProblem < Gecode::Model
|
4
|
+
attr :b1
|
5
|
+
attr :b2
|
6
|
+
attr :b3
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@b1 = self.bool_var
|
10
|
+
@b2 = self.bool_var
|
11
|
+
@b3 = self.bool_var
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Gecode::Constraints::Bool do
|
16
|
+
before do
|
17
|
+
@model = BoolSampleProblem.new
|
18
|
+
@b1 = @model.b1
|
19
|
+
@b2 = @model.b2
|
20
|
+
@b3 = @model.b3
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should handle single variables constrainted to be true' do
|
24
|
+
@b1.must_be.true
|
25
|
+
b1 = @model.solve!.b1
|
26
|
+
b1.should be_assigned
|
27
|
+
b1.true?.should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should handle single variables constrainted to be false' do
|
31
|
+
@b1.must_be.false
|
32
|
+
b1 = @model.solve!.b1
|
33
|
+
b1.should be_assigned
|
34
|
+
b1.true?.should_not be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should handle single variables constrainted not to be true' do
|
38
|
+
@b1.must_not_be.false
|
39
|
+
b1 = @model.solve!.b1
|
40
|
+
b1.should be_assigned
|
41
|
+
b1.true?.should be_true
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should handle single variables constrainted not to be false' do
|
45
|
+
@b1.must_not_be.true
|
46
|
+
b1 = @model.solve!.b1
|
47
|
+
b1.should be_assigned
|
48
|
+
b1.true?.should_not be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should handle disjunction' do
|
52
|
+
@b1.must_be.false
|
53
|
+
(@b1 | @b2).must_be.true
|
54
|
+
sol = @model.solve!
|
55
|
+
sol.b1.true?.should_not be_true
|
56
|
+
sol.b2.true?.should be_true
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should handle negated disjunction' do
|
60
|
+
@b1.must_be.false
|
61
|
+
(@b1 | @b2).must_not_be.true
|
62
|
+
sol = @model.solve!
|
63
|
+
sol.b1.true?.should_not be_true
|
64
|
+
sol.b2.true?.should_not be_true
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should handle conjunction' do
|
68
|
+
(@b1 & @b2).must_be.true
|
69
|
+
sol = @model.solve!
|
70
|
+
sol.b1.true?.should be_true
|
71
|
+
sol.b2.true?.should be_true
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should handle negated conjunction' do
|
75
|
+
@b1.must_be.true
|
76
|
+
(@b1 & @b2).must_not_be.true
|
77
|
+
sol = @model.solve!
|
78
|
+
sol.b1.true?.should be_true
|
79
|
+
sol.b2.true?.should_not be_true
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should handle single variables as right hand side' do
|
83
|
+
@b1.must == @b2
|
84
|
+
@b2.must_be.false
|
85
|
+
sol = @model.solve!
|
86
|
+
sol.b1.true?.should_not be_true
|
87
|
+
sol.b2.true?.should_not be_true
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should handle single variables with negation as right hand side' do
|
91
|
+
@b1.must_not == @b2
|
92
|
+
@b2.must_be.false
|
93
|
+
sol = @model.solve!
|
94
|
+
sol.b1.true?.should be_true
|
95
|
+
sol.b2.true?.should_not be_true
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should handle expressions as right hand side' do
|
99
|
+
@b1.must == (@b2 | @b3)
|
100
|
+
@b2.must_be.true
|
101
|
+
sol = @model.solve!
|
102
|
+
sol.b1.true?.should be_true
|
103
|
+
sol.b2.true?.should be_true
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should handle nested expressions as left hand side' do
|
107
|
+
((@b1 & @b2) | @b3 | (@b1 & @b3)).must_be.true
|
108
|
+
@b1.must_be.false
|
109
|
+
sol = @model.solve!
|
110
|
+
sol.b1.true?.should_not be_true
|
111
|
+
sol.b3.true?.should be_true
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should handle nested expressions on both side' do
|
115
|
+
((@b1 & @b1) | @b3).must == ((@b1 & @b3) & @b2)
|
116
|
+
@b1.must_be.true
|
117
|
+
sol = @model.solve!
|
118
|
+
sol.b1.true?.should be_true
|
119
|
+
sol.b2.true?.should be_true
|
120
|
+
sol.b3.true?.should be_true
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should handle nested expressions on both sides with negation' do
|
124
|
+
((@b1 & @b1) | @b3).must_not == ((@b1 | @b3) & @b2)
|
125
|
+
@b1.must_be.true
|
126
|
+
@b3.must_be.true
|
127
|
+
sol = @model.solve!
|
128
|
+
sol.b1.true?.should be_true
|
129
|
+
sol.b2.true?.should_not be_true
|
130
|
+
sol.b3.true?.should be_true
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/constraint_helper'
|
3
|
+
|
4
|
+
class ChannelSampleProblem < Gecode::Model
|
5
|
+
attr :elements
|
6
|
+
attr :positions
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@elements = int_var_array(4, 0..3)
|
10
|
+
@elements.must_be.distinct
|
11
|
+
@positions = int_var_array(4, 0..3)
|
12
|
+
@positions.must_be.distinct
|
13
|
+
branch_on @elements
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe Gecode::Constraints::IntEnum::Channel do
|
18
|
+
before do
|
19
|
+
@model = ChannelSampleProblem.new
|
20
|
+
@positions = @model.positions
|
21
|
+
@elements = @model.elements
|
22
|
+
@invoke_options = lambda do |hash|
|
23
|
+
@positions.must.channel @elements, hash
|
24
|
+
@model.solve!
|
25
|
+
end
|
26
|
+
@expect_options = lambda do |strength, reif_var|
|
27
|
+
Gecode::Raw.should_receive(:channel).once.with(@model.active_space,
|
28
|
+
an_instance_of(Gecode::Raw::IntVarArray),
|
29
|
+
an_instance_of(Gecode::Raw::IntVarArray), strength)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should translate into a channel constraint' do
|
34
|
+
Gecode::Raw.should_receive(:channel).once.with(@model.active_space,
|
35
|
+
anything, anything, Gecode::Raw::ICL_DEF)
|
36
|
+
@invoke_options.call({})
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should constrain variables to be channelled' do
|
40
|
+
@elements.must.channel @positions
|
41
|
+
@model.solve!
|
42
|
+
elements = @model.elements.map{ |e| e.val }
|
43
|
+
positions = @model.elements.map{ |p| p.val }
|
44
|
+
elements.each_with_index do |element, i|
|
45
|
+
element.should equal(positions.index(i))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should not allow negation' do
|
50
|
+
lambda{ @elements.must_not.channel @positions }.should raise_error(
|
51
|
+
Gecode::MissingConstraintError)
|
52
|
+
end
|
53
|
+
|
54
|
+
it_should_behave_like 'constraint with strength option'
|
55
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
# This requires that the constraint spec has instance variables @invoke_options
|
4
|
+
# and @expect_options .
|
5
|
+
describe 'constraint with strength option', :shared => true do
|
6
|
+
{ :default => Gecode::Raw::ICL_DEF,
|
7
|
+
:value => Gecode::Raw::ICL_VAL,
|
8
|
+
:bounds => Gecode::Raw::ICL_BND,
|
9
|
+
:domain => Gecode::Raw::ICL_DOM
|
10
|
+
}.each_pair do |name, gecode_value|
|
11
|
+
it "should translate propagation strength #{name}" do
|
12
|
+
@expect_options.call(gecode_value, nil)
|
13
|
+
@invoke_options.call(:strength => name)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should default to using default as propagation strength' do
|
18
|
+
@expect_options.call(Gecode::Raw::ICL_DEF, nil)
|
19
|
+
@invoke_options.call({})
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should raise errors for unrecognized options' do
|
23
|
+
lambda{ @invoke_options.call(:does_not_exist => :foo) }.should(
|
24
|
+
raise_error(ArgumentError))
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should raise errors for unrecognized propagation strengths' do
|
28
|
+
lambda{ @invoke_options.call(:strength => :does_not_exist) }.should(
|
29
|
+
raise_error(ArgumentError))
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should raise errors for reification variables of incorrect type' do
|
33
|
+
lambda{ @invoke_options.call(:reify => 'foo') }.should(
|
34
|
+
raise_error(TypeError))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# This requires that the constraint spec has instance variables @invoke_options
|
39
|
+
# and @expect_options .
|
40
|
+
describe 'constraint with options', :shared => true do
|
41
|
+
it 'should translate reification' do
|
42
|
+
var = @model.bool_var
|
43
|
+
@expect_options.call(Gecode::Raw::ICL_DEF, var)
|
44
|
+
@invoke_options.call(:reify => var)
|
45
|
+
end
|
46
|
+
|
47
|
+
it_should_behave_like 'constraint with strength option'
|
48
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Gecode::Constraints::Expression do
|
4
|
+
it 'should raise error if it doesn\'t get all parameters for initialization' do
|
5
|
+
lambda do
|
6
|
+
Gecode::Constraints::Expression.new(Gecode::Model.new, :negate => false)
|
7
|
+
end.should raise_error(ArgumentError)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Gecode::Constraints::IntEnum::Expression do
|
12
|
+
it 'should raise error unless lhs is an enum' do
|
13
|
+
lambda do
|
14
|
+
Gecode::Constraints::IntEnum::Expression.new(Gecode::Model.new,
|
15
|
+
:lhs => 'foo', :negate => false)
|
16
|
+
end.should raise_error(TypeError)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe Gecode::Constraints::Constraint, ' (not subclassed)' do
|
21
|
+
before do
|
22
|
+
@con = Gecode::Constraints::Constraint.new(Gecode::Model.new, {})
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should raise error when calling #post because it\'s not overridden' do
|
26
|
+
lambda{ @con.post }.should raise_error(NoMethodError)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/constraint_helper'
|
3
|
+
|
4
|
+
class CountSampleProblem < Gecode::Model
|
5
|
+
attr :list
|
6
|
+
attr :element
|
7
|
+
attr :target
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@list = int_var_array(4, 0..3)
|
11
|
+
@element = int_var(0..3)
|
12
|
+
@target = int_var(0..4)
|
13
|
+
branch_on @list
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe Gecode::Constraints::IntEnum::Count do
|
18
|
+
before do
|
19
|
+
@model = CountSampleProblem.new
|
20
|
+
@list = @model.list
|
21
|
+
@element = @model.element
|
22
|
+
@target = @model.target
|
23
|
+
|
24
|
+
# Creates an expectation corresponding to the specified input.
|
25
|
+
@expect = lambda do |element, relation, target, strength, reif_var|
|
26
|
+
target = target.bind if target.respond_to? :bind
|
27
|
+
element = element.bind if element.respond_to? :bind
|
28
|
+
if reif_var.nil?
|
29
|
+
Gecode::Raw.should_receive(:count).once.with(@model.active_space,
|
30
|
+
an_instance_of(Gecode::Raw::IntVarArray),
|
31
|
+
element, relation, target, strength)
|
32
|
+
else
|
33
|
+
Gecode::Raw.should_receive(:count).once.with(@model.active_space,
|
34
|
+
an_instance_of(Gecode::Raw::IntVarArray),
|
35
|
+
element, Gecode::Raw::IRT_EQ,
|
36
|
+
an_instance_of(Gecode::Raw::IntVar), strength)
|
37
|
+
Gecode::Raw.should_receive(:rel).once.with(@model.active_space,
|
38
|
+
an_instance_of(Gecode::Raw::IntVar), relation,
|
39
|
+
target, reif_var.bind, strength)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# For constraint option spec.
|
44
|
+
@invoke_options = lambda do |hash|
|
45
|
+
@list.count(@element).must_be.greater_than(@target, hash)
|
46
|
+
@model.solve!
|
47
|
+
end
|
48
|
+
@expect_options = lambda do |strength, reif_var|
|
49
|
+
@expect.call(@element, Gecode::Raw::IRT_GR, @target, strength, reif_var)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Various situations that must be handled, nil denotes that a variable should
|
54
|
+
# be used.
|
55
|
+
situations = {
|
56
|
+
'variable element and target' => [nil, nil],
|
57
|
+
'variable element and constant target' => [nil, 2],
|
58
|
+
'constant element and variable target' => [1, nil],
|
59
|
+
'constant element and constant target' => [1, 2]
|
60
|
+
}.each_pair do |description, element_and_target|
|
61
|
+
element, target = element_and_target
|
62
|
+
Gecode::Constraints::Util::RELATION_TYPES.each_pair do |relation, type|
|
63
|
+
it "should translate #{relation} with #{description}" do
|
64
|
+
element = @element if element.nil?
|
65
|
+
target = @target if target.nil?
|
66
|
+
@expect.call(element, type, target, Gecode::Raw::ICL_DEF, nil)
|
67
|
+
@list.count(element).must.send(relation, target)
|
68
|
+
@model.solve!
|
69
|
+
end
|
70
|
+
end
|
71
|
+
Gecode::Constraints::Util::NEGATED_RELATION_TYPES.each_pair do |relation, type|
|
72
|
+
it "should translate negated #{relation} with #{description}" do
|
73
|
+
element = @element if element.nil?
|
74
|
+
target = @target if target.nil?
|
75
|
+
@expect.call(element, type, target, Gecode::Raw::ICL_DEF, nil)
|
76
|
+
@list.count(element).must_not.send(relation, target)
|
77
|
+
@model.solve!
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should raise error if the target is of the wrong type' do
|
83
|
+
lambda{ @list.count(@element).must == 'hello' }.should raise_error(
|
84
|
+
TypeError)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should raise error on element is of the wrong type' do
|
88
|
+
lambda{ @list.count('foo').must == @target }.should raise_error(
|
89
|
+
TypeError)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should constrain the count' do
|
93
|
+
@list.must_be.distinct
|
94
|
+
@list.count(0).must <= 0
|
95
|
+
@model.solve!.should be_nil
|
96
|
+
end
|
97
|
+
|
98
|
+
it_should_behave_like 'constraint with options'
|
99
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/constraint_helper'
|
3
|
+
|
4
|
+
class DistinctSampleProblem < Gecode::Model
|
5
|
+
attr :vars
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@vars = int_var_array(2, 1)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe Gecode::Constraints::IntEnum::Distinct do
|
13
|
+
before do
|
14
|
+
@model = DistinctSampleProblem.new
|
15
|
+
@invoke_options = lambda do |hash|
|
16
|
+
@model.vars.must_be.distinct(hash)
|
17
|
+
@model.solve!
|
18
|
+
end
|
19
|
+
@expect_options = lambda do |strength, reif_var|
|
20
|
+
Gecode::Raw.should_receive(:distinct).once.with(@model.active_space,
|
21
|
+
an_instance_of(Gecode::Raw::IntVarArray), strength)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should translate into a distinct constraint' do
|
26
|
+
Gecode::Raw.should_receive(:distinct).once.with(@model.active_space,
|
27
|
+
anything, Gecode::Raw::ICL_DEF)
|
28
|
+
@invoke_options.call({})
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should constrain variables to be distinct' do
|
32
|
+
# This won't work well without branching or propagation strengths. So this
|
33
|
+
# just shows that the distinct constraint will cause trivially unsolvable
|
34
|
+
# problems to directly fail.
|
35
|
+
@model.vars.must_be.distinct
|
36
|
+
@model.solve!.should be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should not allow negation' do
|
40
|
+
lambda{ @model.vars.must_not_be.distinct }.should raise_error(
|
41
|
+
Gecode::MissingConstraintError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it_should_behave_like 'constraint with strength option'
|
45
|
+
end
|
46
|
+
|
47
|
+
describe Gecode::Constraints::IntEnum::Distinct, ' (with offsets)' do
|
48
|
+
before do
|
49
|
+
@model = DistinctSampleProblem.new
|
50
|
+
@invoke_options = lambda do |hash|
|
51
|
+
@model.vars.with_offsets(1,2).must_be.distinct(hash)
|
52
|
+
@model.solve!
|
53
|
+
end
|
54
|
+
@expect_options = lambda do |strength, reif_var|
|
55
|
+
if reif_var.nil?
|
56
|
+
Gecode::Raw.should_receive(:distinct).once.with(@model.active_space,
|
57
|
+
anything, an_instance_of(Gecode::Raw::IntVarArray), strength)
|
58
|
+
else
|
59
|
+
Gecode::Raw.should_receive(:distinct).once.with(@model.active_space,
|
60
|
+
anything, an_instance_of(Gecode::Raw::IntVarArray), strength,
|
61
|
+
an_instance_of(Gecode::Raw::BoolVar))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should translate into a distinct constraint with offsets' do
|
67
|
+
Gecode::Raw.should_receive(:distinct).once.with(@model.active_space,
|
68
|
+
anything, anything, Gecode::Raw::ICL_DEF)
|
69
|
+
@invoke_options.call({})
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should consider offsets when making variables distinct' do
|
73
|
+
@model.vars.with_offsets(-1,0).must_be.distinct
|
74
|
+
x,y = @model.solve!.vars
|
75
|
+
x.val.should equal(1)
|
76
|
+
y.val.should equal(1)
|
77
|
+
end
|
78
|
+
|
79
|
+
# This tests two distinct in conjunction. It's here because of a bug found.
|
80
|
+
it 'should play nice with normal distinct' do
|
81
|
+
@model.vars.with_offsets(-1,0).must_be.distinct
|
82
|
+
@model.vars.must_be.distinct
|
83
|
+
@model.solve!.should be_nil
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should accept an array as offsets' do
|
87
|
+
@model.vars.with_offsets([-1,0]).must_be.distinct
|
88
|
+
x,y = @model.solve!.vars
|
89
|
+
x.val.should equal(1)
|
90
|
+
y.val.should equal(1)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should not allow negation' do
|
94
|
+
lambda{ @model.vars.with_offsets(1,2).must_not_be.distinct }.should
|
95
|
+
raise_error(Gecode::MissingConstraintError)
|
96
|
+
end
|
97
|
+
|
98
|
+
it_should_behave_like 'constraint with strength option'
|
99
|
+
end
|