specs/constraints/channel.rb in gecoder-0.8.1 vs specs/constraints/channel.rb in gecoder-0.8.2
- old
+ new
@@ -14,10 +14,24 @@
@sets = set_var_array(4, [], 0..3)
branch_on @positions
end
end
+class BoolChannelSampleProblem < Gecode::Model
+ attr :bool_enum
+ attr :bool
+ attr :int
+
+ def initialize
+ @bool_enum = bool_var_array(4)
+ @int = int_var(0..3)
+ @bool = bool_var
+
+ branch_on wrap_enum([@int])
+ end
+end
+
describe Gecode::Constraints::IntEnum::Channel, ' (two int enums)' do
before do
@model = ChannelSampleProblem.new
@positions = @model.positions
@elements = @model.elements
@@ -65,17 +79,25 @@
describe Gecode::Constraints::IntEnum::Channel, ' (one int enum and one set enum)' do
before do
@model = ChannelSampleProblem.new
@positions = @model.positions
@sets = @model.sets
+
+ @invoke_options = lambda do |hash|
+ @positions.must.channel @sets, hash
+ @model.solve!
+ end
+ @expect_options = option_expectation do |strength, kind, reif_var|
+ Gecode::Raw.should_receive(:channel).once.with(
+ an_instance_of(Gecode::Raw::Space),
+ an_instance_of(Gecode::Raw::IntVarArray),
+ an_instance_of(Gecode::Raw::SetVarArray))
+ end
end
it 'should translate into a channel constraint' do
- Gecode::Raw.should_receive(:channel).once.with(
- an_instance_of(Gecode::Raw::Space),
- an_instance_of(Gecode::Raw::IntVarArray),
- an_instance_of(Gecode::Raw::SetVarArray))
+ @expect_options.call({})
@positions.must.channel @sets
@model.solve!
end
it 'should constrain variables to be channelled' do
@@ -85,10 +107,12 @@
positions = @model.positions.values
positions.each_with_index do |position, i|
sets[position].value.should include(i)
end
end
+
+ it_should_behave_like 'non-reifiable set constraint'
end
describe Gecode::Constraints::SetEnum, ' (channel with set as left hand side)' do
before do
@model = ChannelSampleProblem.new
@@ -121,6 +145,212 @@
it 'should raise error for unsupported right hand sides' do
lambda{ @sets.must.channel 'hello' }.should raise_error(TypeError)
end
it_should_behave_like 'non-reifiable set constraint'
-end
\ No newline at end of file
+end
+
+# Requires @model, @bool and @int. Also requires @place_constraint which is a
+# method that takes five variables: a boolean variable, an integer variable,
+# the name of the equality method to use, whether or not the constraint should
+# be negated and a hash of options, and places the channel constraint on them.
+describe 'channel constraint between one int and one bool variable', :shared => true do
+ before do
+ @invoke_options = lambda do |hash|
+ @place_constraint.call(@bool, @int, :==, false, hash)
+ @model.solve!
+ end
+ @expect_options = option_expectation do |strength, kind, reif_var|
+ Gecode::Raw.should_receive(:channel).once.with(
+ an_instance_of(Gecode::Raw::Space),
+ an_instance_of(Gecode::Raw::IntVar),
+ an_instance_of(Gecode::Raw::BoolVar),
+ strength, kind)
+ end
+ end
+
+ ([:==] + Gecode::Constraints::Util::COMPARISON_ALIASES[:==]).each do |ali|
+ it "should translate #{ali} into a channel constraint" do
+ @expect_options.call({})
+ @place_constraint.call(@bool, @int, ali, false, {})
+ @model.solve!
+ end
+ end
+
+ it 'should constrain the int variable to be 1 when the boolean variable is true' do
+ @bool.must_be.true
+ @place_constraint.call(@bool, @int, :==, false, {})
+ @model.solve!
+ @int.value.should == 1
+ end
+
+ it 'should constrain the int variable to be 0 when the boolean variable is false' do
+ @bool.must_be.false
+ @place_constraint.call(@bool, @int, :==, false, {})
+ @model.solve!
+ @int.value.should == 0
+ end
+
+ it 'should not allow negation' do
+ lambda do
+ @place_constraint.call(@bool, @int, :==, true, {})
+ end.should raise_error(Gecode::MissingConstraintError)
+ end
+
+ it_should_behave_like 'non-reifiable constraint'
+end
+
+describe Gecode::Constraints::Int::Channel, ' (one int and one bool variable)' do
+ before do
+ @model = BoolChannelSampleProblem.new
+ @bool = @model.bool_var
+ @int = @model.int_var
+
+ @place_constraint = lambda do |bool, int, equals_method_name, negate, options|
+ if negate
+ int.must_not.method(equals_method_name).call(bool, options)
+ else
+ int.must.method(equals_method_name).call(bool, options)
+ end
+ end
+ end
+
+ it 'should not shadow linear boolean constraints' do
+ lambda do
+ (@bool + @bool).must == @bool
+ @model.solve!
+ end.should_not raise_error
+ end
+
+ it 'should raise error for unsupported right hand sides' do
+ lambda{ @int.must == 'hello' }.should raise_error(TypeError)
+ end
+
+ it_should_behave_like 'channel constraint between one int and one bool variable'
+end
+
+describe Gecode::Constraints::Int::Channel, ' (one bool and one int variable)' do
+ before do
+ @model = BoolChannelSampleProblem.new
+ @bool = @model.bool_var
+ @int = @model.int_var
+
+ @place_constraint = lambda do |bool, int, equals_method_name, negate, options|
+ if negate
+ bool.must_not.method(equals_method_name).call(int, options)
+ else
+ bool.must.method(equals_method_name).call(int, options)
+ end
+ end
+ end
+
+ it 'should not shadow linear boolean constraints' do
+ lambda do
+ @bool.must == @bool + @bool
+ @model.solve!
+ end.should_not raise_error
+ end
+
+ it 'should raise error for unsupported right hand sides' do
+ lambda{ @bool.must == 'hello' }.should raise_error(TypeError)
+ end
+
+ it_should_behave_like 'channel constraint between one int and one bool variable'
+end
+
+# Requires @model, @bool_enum and @int. Also requires @place_constraint which
+# is a method that takes four variables: a boolean enum, an integer variable,
+# whether or not the constraint should be negated and a hash of options, and
+# places the channel constraint on them.
+describe 'channel constraint between bool enum and int variable', :shared => true do
+ before do
+ @invoke_options = lambda do |hash|
+ @place_constraint.call(@bools, @int, false, hash)
+ @model.solve!
+ end
+ @expect_options = option_expectation do |strength, kind, reif_var|
+ Gecode::Raw.should_receive(:channel).once.with(
+ an_instance_of(Gecode::Raw::Space),
+ an_instance_of(Gecode::Raw::BoolVarArray),
+ an_instance_of(Gecode::Raw::IntVar), 0,
+ strength, kind)
+ end
+ end
+
+ it 'should channel the bool enum with the integer variable' do
+ @int.must > 2
+ @place_constraint.call(@bools, @int, false, {})
+ @model.solve!.should_not be_nil
+ int_val = @int.value
+ @bools.values.each_with_index do |bool, index|
+ bool.should == (index == int_val)
+ end
+ end
+
+ it 'should take the offset into account when channeling' do
+ @int.must > 2
+ offset = 1
+ @place_constraint.call(@bools, @int, false, :offset => offset)
+ @model.solve!.should_not be_nil
+ int_val = @int.value
+ @bools.values.each_with_index do |bool, index|
+ bool.should == (index + offset == int_val)
+ end
+ end
+
+ it 'should not allow negation' do
+ lambda do
+ @place_constraint.call(@bools, @int, true, {})
+ end.should raise_error(Gecode::MissingConstraintError)
+ end
+
+ it_should_behave_like 'non-reifiable constraint'
+end
+
+describe Gecode::Constraints::BoolEnum::Channel, ' (bool enum as lhs with int variable)' do
+ before do
+ @model = BoolChannelSampleProblem.new
+ @bools = @model.bool_enum
+ @int = @model.int
+
+ @place_constraint = lambda do |bools, int, negate, options|
+ unless negate
+ bools.must.channel(int, options)
+ else
+ bools.must_not.channel(int, options)
+ end
+ end
+ end
+
+ it 'should raise error if an integer variable is not given as right hand side' do
+ lambda do
+ @bools.must.channel 'hello'
+ end.should raise_error(TypeError)
+ end
+
+ it_should_behave_like 'channel constraint between bool enum and int variable'
+end
+
+
+describe Gecode::Constraints::BoolEnum::Channel, ' (int variable as lhs with bool enum)' do
+ before do
+ @model = BoolChannelSampleProblem.new
+ @bools = @model.bool_enum
+ @int = @model.int
+
+ @place_constraint = lambda do |bools, int, negate, options|
+ unless negate
+ int.must.channel(bools, options)
+ else
+ int.must_not.channel(bools, options)
+ end
+ end
+ end
+
+ it 'should raise error if a boolean enum is not given as right hand side' do
+ lambda do
+ @int.must.channel 'hello'
+ end.should raise_error(TypeError)
+ end
+
+ it_should_behave_like 'channel constraint between bool enum and int variable'
+end