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