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.
Files changed (56) hide show
  1. data/CHANGES +17 -2
  2. data/README +7 -1
  3. data/Rakefile +4 -0
  4. data/lib/gecoder/interface/constraints/bool/boolean.rb +1 -4
  5. data/lib/gecoder/interface/constraints/int/arithmetic.rb +77 -0
  6. data/lib/gecoder/interface/constraints/int/domain.rb +50 -0
  7. data/lib/gecoder/interface/constraints/int/linear.rb +12 -44
  8. data/lib/gecoder/interface/constraints/int_enum/arithmetic.rb +72 -0
  9. data/lib/gecoder/interface/constraints/int_enum/channel.rb +32 -0
  10. data/lib/gecoder/interface/constraints/int_enum/count.rb +90 -0
  11. data/lib/gecoder/interface/constraints/int_enum/distinct.rb +3 -8
  12. data/lib/gecoder/interface/constraints/int_enum/element.rb +75 -0
  13. data/lib/gecoder/interface/constraints/int_enum/equality.rb +31 -0
  14. data/lib/gecoder/interface/constraints/int_enum/sort.rb +104 -0
  15. data/lib/gecoder/interface/constraints/int_enum_constraints.rb +6 -0
  16. data/lib/gecoder/interface/constraints/int_var_constraints.rb +2 -0
  17. data/lib/gecoder/interface/constraints/reifiable_constraints.rb +21 -0
  18. data/lib/gecoder/interface/constraints.rb +57 -6
  19. data/lib/gecoder/interface/enum_matrix.rb +64 -0
  20. data/lib/gecoder/interface/enum_wrapper.rb +33 -5
  21. data/lib/gecoder/interface/model.rb +36 -6
  22. data/lib/gecoder/interface.rb +1 -0
  23. data/lib/gecoder/version.rb +4 -0
  24. data/lib/gecoder.rb +1 -0
  25. data/specs/binding_changes.rb +72 -0
  26. data/specs/bool_var.rb +20 -0
  27. data/specs/branch.rb +104 -0
  28. data/specs/constraints/arithmetic.rb +227 -0
  29. data/specs/constraints/boolean.rb +132 -0
  30. data/specs/constraints/channel.rb +55 -0
  31. data/specs/constraints/constraint_helper.rb +48 -0
  32. data/specs/constraints/constraints.rb +28 -0
  33. data/specs/constraints/count.rb +99 -0
  34. data/specs/constraints/distinct.rb +99 -0
  35. data/specs/constraints/domain.rb +56 -0
  36. data/specs/constraints/element.rb +128 -0
  37. data/specs/constraints/equality.rb +30 -0
  38. data/specs/constraints/linear.rb +166 -0
  39. data/specs/constraints/reification_sugar.rb +92 -0
  40. data/specs/constraints/relation.rb +72 -0
  41. data/specs/constraints/sort.rb +173 -0
  42. data/specs/enum_matrix.rb +43 -0
  43. data/specs/enum_wrapper.rb +100 -0
  44. data/specs/int_var.rb +108 -0
  45. data/specs/model.rb +84 -0
  46. data/specs/search.rb +157 -0
  47. data/specs/spec_helper.rb +63 -0
  48. data/specs/tmp +135 -0
  49. data/tasks/all_tasks.rb +1 -0
  50. data/tasks/distribution.rake +64 -0
  51. data/tasks/rcov.rake +17 -0
  52. data/tasks/specs.rake +16 -0
  53. data/tasks/svn.rake +11 -0
  54. data/tasks/website.rake +58 -0
  55. data/vendor/rust/include/rust_conversions.hh +1 -2
  56. metadata +53 -2
@@ -0,0 +1,104 @@
1
+ module Gecode::Constraints::IntEnum
2
+ class Expression
3
+ # Initiates a sort constraint. Beyond the common options the sort constraint
4
+ # can also take the following options:
5
+ #
6
+ # [:as] Defines a target (must be an int variable enumerable) that will
7
+ # hold the sorted version of the original enumerable. The original
8
+ # enumerable will not be affected (i.e. will not necessarily be
9
+ # sorted)
10
+ # [:order] Sets an int variable enumerable that should be used to store the
11
+ # order of the original enum's variables when sorted. The original
12
+ # enumerable will not be affected (i.e. will not necessarily be
13
+ # sorted)
14
+ #
15
+ # If neither of those options are specified then the original enumerable
16
+ # will be constrained to be sorted (otherwise not). Sort constraints with
17
+ # options do not allow negation.
18
+ def sorted(options = {})
19
+ # Extract and check options.
20
+ target = options.delete(:as)
21
+ order = options.delete(:order)
22
+ unless target.nil? or target.respond_to? :to_int_var_array
23
+ raise TypeError, 'Expected int var enum as :as, got ' +
24
+ "#{target.class}."
25
+ end
26
+ unless order.nil? or order.respond_to? :to_int_var_array
27
+ raise TypeError, 'Expected int var enum as :order, got ' +
28
+ "#{order.class}."
29
+ end
30
+
31
+ # Extract standard options and convert to constraint.
32
+ @params.update(Gecode::Constraints::Util.decode_options(options))
33
+ if target.nil? and order.nil?
34
+ @model.add_constraint Sort::SortConstraint.new(@model, @params)
35
+ else
36
+ # Do not allow negation.
37
+ if @params[:negate]
38
+ raise Gecode::MissingConstraintError, 'A negated sort with options ' +
39
+ 'is not implemented.'
40
+ end
41
+
42
+ @params.update(:target => target, :order => order)
43
+ @model.add_constraint Sort::SortConstraintWithOptions.new(@model,
44
+ @params)
45
+ end
46
+ end
47
+ end
48
+
49
+ # A module that gathers the classes and modules used in sort constraints.
50
+ module Sort
51
+ # Describes a sort constraint with target and order.
52
+ class SortConstraintWithOptions < Gecode::Constraints::Constraint
53
+ def post
54
+ if @params[:target].nil?
55
+ # We must have a target.
56
+ lhs = @params[:lhs]
57
+ @params[:target] = @model.int_var_array(lhs.size, lhs.domain_range)
58
+ end
59
+
60
+ # Prepare the parameters.
61
+ params = @params.values_at(:lhs, :target, :order, :strength).map do |param|
62
+ if param.respond_to? :to_int_var_array
63
+ param.to_int_var_array
64
+ else
65
+ param
66
+ end
67
+ end.delete_if{ |param| param.nil? }
68
+ # Post the constraint.
69
+ Gecode::Raw::sortedness(@model.active_space, *params)
70
+ end
71
+ end
72
+
73
+ # Describes a sort constraint.
74
+ class SortConstraint < Gecode::Constraints::ReifiableConstraint
75
+ def post
76
+ lhs, strength, reif_var = @params.values_at(:lhs, :strength, :reif)
77
+ using_reification = !reif_var.nil?
78
+
79
+ # We translate the constraint into n-1 relation constraints.
80
+ options = {:strength =>
81
+ Gecode::Constraints::Util::PROPAGATION_STRENGTHS.invert[strength]}
82
+ if using_reification
83
+ reification_variables = @model.bool_var_array(lhs.size - 1)
84
+ end
85
+ (lhs.size - 1).times do |i|
86
+ first, second = lhs[i, 2]
87
+ rel_options = options.clone
88
+ if using_reification
89
+ # Reify each relation constraint and then bind them all together.
90
+ rel_options[:reify] = reification_variables[i]
91
+ end
92
+ first.must_be.less_than_or_equal_to(second, rel_options)
93
+ end
94
+ if using_reification
95
+ # TODO use the interface's all constraint when available.
96
+ # reification_variables.all.must == reif_var
97
+ Gecode::Raw::bool_and(@model.active_space,
98
+ reification_variables.to_bool_var_array, reif_var.bind, strength)
99
+ end
100
+ end
101
+ negate_using_reification
102
+ end
103
+ end
104
+ end
@@ -29,3 +29,9 @@ module Gecode
29
29
  end
30
30
 
31
31
  require 'gecoder/interface/constraints/int_enum/distinct'
32
+ require 'gecoder/interface/constraints/int_enum/equality'
33
+ require 'gecoder/interface/constraints/int_enum/channel'
34
+ require 'gecoder/interface/constraints/int_enum/element'
35
+ require 'gecoder/interface/constraints/int_enum/count'
36
+ require 'gecoder/interface/constraints/int_enum/sort'
37
+ require 'gecoder/interface/constraints/int_enum/arithmetic'
@@ -20,3 +20,5 @@ module Gecode
20
20
  end
21
21
 
22
22
  require 'gecoder/interface/constraints/int/linear'
23
+ require 'gecoder/interface/constraints/int/domain'
24
+ require 'gecoder/interface/constraints/int/arithmetic'
@@ -53,5 +53,26 @@ module Gecode::Constraints
53
53
  end
54
54
  yield(con1_holds, con2_holds)
55
55
  end
56
+
57
+ # If called the negation of the constraint will be handled using the
58
+ # reification variable. This means that the post method (which has to be
59
+ # defined prior to calling this method) doesn't have to bother about
60
+ # negation.
61
+ def self.negate_using_reification
62
+ class_eval do
63
+ alias_method :post_without_negation, :post
64
+
65
+ def post
66
+ if @params[:negate]
67
+ if @params[:reif].nil?
68
+ # Create a reification variable if none exists.
69
+ @params[:reif] = @model.bool_var
70
+ end
71
+ @params[:reif].must_be.false
72
+ end
73
+ post_without_negation
74
+ end
75
+ end
76
+ end
56
77
  end
57
78
  end
@@ -50,6 +50,35 @@ module Gecode
50
50
  @model = model
51
51
  @params = params
52
52
  end
53
+
54
+ private
55
+
56
+ # Creates aliases for any defined comparison methods.
57
+ def self.alias_comparison_methods
58
+ Gecode::Constraints::Util::COMPARISON_ALIASES.each_pair do |orig, aliases|
59
+ if instance_methods.include?(orig.to_s)
60
+ aliases.each do |name|
61
+ alias_method(name, orig)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ # Describes a constraint expression that has yet to be completed. I.e. a
69
+ # form of must has not yet been called, but some method has been called to
70
+ # initiate the expression. An example is distinct with offsets:
71
+ #
72
+ # enum.with_offsets(0..n).must_be.distinct
73
+ #
74
+ # The call of with_offsets initiates the constraint as a stub, even though
75
+ # must has not yet been called.
76
+ class ExpressionStub
77
+ # Constructs a new expression with the specified parameters.
78
+ def initialize(model, params)
79
+ @model = model
80
+ @params = params
81
+ end
53
82
  end
54
83
 
55
84
  # Base class for all constraints.
@@ -68,11 +97,8 @@ module Gecode
68
97
  end
69
98
  end
70
99
 
71
- # A module that provides some utility-methods for decoding options given to
72
- # constraints.
73
- module OptionUtil
74
- private
75
-
100
+ # A module that provides some utility-methods for constraints.
101
+ module Util
76
102
  # Maps the name used in options to the value used in Gecode for
77
103
  # propagation strengths.
78
104
  PROPAGATION_STRENGTHS = {
@@ -82,7 +108,32 @@ module Gecode
82
108
  :domain => Gecode::Raw::ICL_DOM
83
109
  }
84
110
 
85
- public
111
+ # Maps the names of the methods to the corresponding integer relation
112
+ # type in Gecode.
113
+ RELATION_TYPES = {
114
+ :== => Gecode::Raw::IRT_EQ,
115
+ :<= => Gecode::Raw::IRT_LQ,
116
+ :< => Gecode::Raw::IRT_LE,
117
+ :>= => Gecode::Raw::IRT_GQ,
118
+ :> => Gecode::Raw::IRT_GR }
119
+ # The same as above, but negated.
120
+ NEGATED_RELATION_TYPES = {
121
+ :== => Gecode::Raw::IRT_NQ,
122
+ :<= => Gecode::Raw::IRT_GR,
123
+ :< => Gecode::Raw::IRT_GQ,
124
+ :>= => Gecode::Raw::IRT_LE,
125
+ :> => Gecode::Raw::IRT_LQ
126
+ }
127
+
128
+ # Various method aliases for comparison methods. Maps the original
129
+ # (symbol) name to an array of aliases.
130
+ COMPARISON_ALIASES = {
131
+ :== => [:equal, :equal_to],
132
+ :> => [:greater, :greater_than],
133
+ :>= => [:greater_or_equal, :greater_than_or_equal_to],
134
+ :< => [:less, :less_than],
135
+ :<= => [:less_or_equal, :less_than_or_equal_to]
136
+ }
86
137
 
87
138
  module_function
88
139
 
@@ -0,0 +1,64 @@
1
+ require 'matrix'
2
+
3
+ module Gecode::Util
4
+ # Methods that make a matrix an enumerable.
5
+ module MatrixEnumMethods
6
+ include Enumerable
7
+
8
+ # Iterates over every element in the matrix.
9
+ def each(&block)
10
+ row_size.times do |i|
11
+ column_size.times do |j|
12
+ yield self[i,j]
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ # Extends Matrix so that it's an enumerable.
19
+ class EnumMatrix < Matrix
20
+ include MatrixEnumMethods
21
+
22
+ def row(i)
23
+ wrap_if_wrapped make_vector_enumerable(super)
24
+ end
25
+
26
+ def column(i)
27
+ wrap_if_wrapped make_vector_enumerable(super)
28
+ end
29
+
30
+ def minor(*args)
31
+ matrix = super
32
+ class <<matrix
33
+ include MatrixEnumMethods
34
+ end
35
+ return wrap_if_wrapped(matrix)
36
+ end
37
+
38
+ private
39
+
40
+ # Makes the specified vector enumerable.
41
+ def make_vector_enumerable(vector)
42
+ class <<vector
43
+ include Enumerable
44
+
45
+ # Iterates over every element in the matrix.
46
+ def each(&block)
47
+ size.times do |i|
48
+ yield self[i]
49
+ end
50
+ end
51
+ end
52
+ return vector
53
+ end
54
+
55
+ # Wraps the specified enumerable if the matrix itself is already wrapped.
56
+ def wrap_if_wrapped(enum)
57
+ if respond_to? :model
58
+ model.wrap_enum(enum)
59
+ else
60
+ enum
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,25 +1,28 @@
1
1
  module Gecode
2
2
  class Model
3
- private
4
-
5
3
  # Wraps a custom enumerable so that constraints can be specified using it.
6
4
  # The argument is altered and returned.
7
5
  def wrap_enum(enum)
8
6
  unless enum.kind_of? Enumerable
9
7
  raise TypeError, 'Only enumerables can be wrapped.'
10
8
  end
11
- if enum.empty?
9
+ elements = enum.to_a
10
+ if elements.empty?
12
11
  raise ArgumentError, 'Enumerable must not be empty.'
13
12
  end
14
13
 
15
- if enum.first.kind_of? FreeIntVar
14
+ if elements.map{ |var| var.kind_of? FreeIntVar }.all?
16
15
  class <<enum
17
16
  include Gecode::IntEnumMethods
18
17
  end
19
- elsif enum.first.kind_of? FreeBoolVar
18
+ elsif elements.map{ |var| var.kind_of? FreeBoolVar }.all?
20
19
  class <<enum
21
20
  include Gecode::BoolEnumMethods
22
21
  end
22
+ elsif elements.map{ |var| var.kind_of? Fixnum }.all?
23
+ class <<enum
24
+ include Gecode::FixnumEnumMethods
25
+ end
23
26
  else
24
27
  raise TypeError, "Enumerable doesn't contain variables #{enum.inspect}."
25
28
  end
@@ -51,6 +54,19 @@ module Gecode
51
54
  return arr
52
55
  end
53
56
  alias_method :to_var_array, :to_int_var_array
57
+
58
+ # Returns the smallest range that contains the domains of all integer
59
+ # variables involved.
60
+ def domain_range
61
+ inject(nil) do |range, var|
62
+ next var.min..var.max if range.nil?
63
+ min = var.min
64
+ max = var.max
65
+ range = min..range.last if min < range.first
66
+ range = range.first..max if max > range.last
67
+ range
68
+ end
69
+ end
54
70
  end
55
71
 
56
72
  # A module containing the methods needed by enumerations containing boolean
@@ -67,4 +83,16 @@ module Gecode
67
83
  end
68
84
  alias_method :to_var_array, :to_bool_var_array
69
85
  end
86
+
87
+ # A module containing the methods needed by enumerations containing fixnums.
88
+ # Requires that it's included in an enumerable.
89
+ module FixnumEnumMethods
90
+ include EnumMethods
91
+
92
+ # Returns the smallest range that contains the domains of all integer
93
+ # variables involved.
94
+ def domain_range
95
+ min..max
96
+ end
97
+ end
70
98
  end
@@ -26,6 +26,24 @@ module Gecode
26
26
  return wrap_enum(variables)
27
27
  end
28
28
 
29
+ # Creates a matrix containing the specified number rows and columns of
30
+ # integer variables with the specified domain. The domain can either be a
31
+ # range or a number of elements.
32
+ def int_var_matrix(row_count, col_count, *domain_args)
33
+ # TODO: Maybe the custom domain should be specified as an array instead?
34
+
35
+ range = domain_range(*domain_args)
36
+ indices = active_space.new_int_vars(range.begin, range.end,
37
+ row_count*col_count)
38
+ rows = []
39
+ row_count.times do |i|
40
+ rows << indices[(i*col_count)...(i.succ*col_count)].map! do |index|
41
+ construct_int_var(index, *domain_args)
42
+ end
43
+ end
44
+ return wrap_enum(Util::EnumMatrix.rows(rows, false))
45
+ end
46
+
29
47
  # Creates a new boolean variable.
30
48
  def bool_var(*domain_args)
31
49
  index = active_space.new_bool_vars.first
@@ -41,6 +59,19 @@ module Gecode
41
59
  return wrap_enum(variables)
42
60
  end
43
61
 
62
+ # Creates a matrix containing the specified number rows and columns of
63
+ # boolean variables.
64
+ def bool_var_matrix(row_count, col_count)
65
+ indices = active_space.new_bool_vars(row_count*col_count)
66
+ rows = []
67
+ row_count.times do |i|
68
+ rows << indices[(i*col_count)...(i.succ*col_count)].map! do |index|
69
+ FreeBoolVar.new(self, index)
70
+ end
71
+ end
72
+ return wrap_enum(Util::EnumMatrix.rows(rows, false))
73
+ end
74
+
44
75
  # Retrieves the currently active space (the one which variables refer to).
45
76
  def active_space
46
77
  @active_space ||= base_space
@@ -97,12 +128,11 @@ module Gecode
97
128
 
98
129
  if domain_args.size > 1
99
130
  # Place an additional domain constraint on the variable with the
100
- # arguments as domain.
101
- # TODO: use the model's way of defining domain constraints when
102
- # available.
103
- domain_set = Gecode::Raw::IntSet.new(domain_args, domain_args.size)
104
- Gecode::Raw::dom(active_space, var.bind, domain_set,
105
- Gecode::Raw::ICL_DEF)
131
+ # arguments as domain. We post it directly since there's no reason not
132
+ # to and the user might otherwise get unexpected domains when inspecting
133
+ # the variable before solving.
134
+ constraint = var.must_be.in domain_args
135
+ @constraints.delete(constraint).post
106
136
  end
107
137
  return var
108
138
  end
@@ -1,5 +1,6 @@
1
1
  require 'gecoder/interface/binding_changes'
2
2
  require 'gecoder/interface/variables'
3
+ require 'gecoder/interface/enum_matrix'
3
4
  require 'gecoder/interface/model'
4
5
  require 'gecoder/interface/search'
5
6
  require 'gecoder/interface/constraints'
@@ -0,0 +1,4 @@
1
+ module GecodeR
2
+ # A string representation of the Gecode/R version.
3
+ VERSION = '0.4.0'
4
+ end
data/lib/gecoder.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__))
2
2
 
3
+ require 'gecoder/version'
3
4
  require 'gecoder/bindings'
4
5
  require 'gecoder/interface'
@@ -0,0 +1,72 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe 'Space', :shared => true do
4
+ it 'should give different indices when creating int variables' do
5
+ @space.new_int_vars(0, 17).should_not equal(@space.new_int_vars(0, 17))
6
+ end
7
+
8
+ it 'should give different indices when creating bool variables' do
9
+ @space.new_bool_vars().should_not equal(@space.new_bool_vars())
10
+ end
11
+
12
+ it 'should give different indices when creating multiple int variables' do
13
+ @space.new_int_vars(0, 17, 17).uniq.size.should equal(17)
14
+ end
15
+
16
+ it 'should give different indices when creating multiple bool variables' do
17
+ @space.new_bool_vars(17).uniq.size.should equal(17)
18
+ end
19
+
20
+ it 'should not return nil for created int variables' do
21
+ @space.new_int_vars(0, 17, 4).each do |i|
22
+ @space.int_var(i).should_not be_nil
23
+ end
24
+ end
25
+
26
+ it 'should not return nil for created int variables' do
27
+ @space.new_bool_vars(4).each do |i|
28
+ @space.bool_var(i).should_not be_nil
29
+ end
30
+ end
31
+
32
+ it 'should return nil when requesting int variables with negative indices' do
33
+ @space.int_var(-1).should be_nil
34
+ end
35
+
36
+ it 'should return nil when requesting bool variables with negative indices' do
37
+ @space.bool_var(-1).should be_nil
38
+ end
39
+ end
40
+
41
+ describe Gecode::Raw::Space, ' (new)' do
42
+ before do
43
+ @space = Gecode::Raw::Space.new
44
+ end
45
+
46
+ it 'should return nil when requesting int variables' do
47
+ @space.int_var(0).should be_nil
48
+ end
49
+
50
+ it 'should return nil when requesting bool variables' do
51
+ @space.bool_var(0).should be_nil
52
+ end
53
+
54
+ it_should_behave_like 'Space'
55
+ end
56
+
57
+ describe Gecode::Raw::Space, ' (with items)' do
58
+ before do
59
+ @space = Gecode::Raw::Space.new
60
+ @first = @space.new_int_vars(1, 4).first
61
+ @second = @space.new_int_vars(-5, 5).first
62
+ end
63
+
64
+ it_should_behave_like 'Space'
65
+
66
+ it 'should give int variables with the correct domains' do
67
+ @space.int_var(@first).min.should equal(1)
68
+ @space.int_var(@first).max.should equal(4)
69
+ @space.int_var(@second).min.should equal(-5)
70
+ @space.int_var(@second).max.should equal(5)
71
+ end
72
+ end
data/specs/bool_var.rb ADDED
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe 'non-empty bool variable', :shared => true do
4
+ it 'should give a NoMethodError when calling a method that doesn\'t exist' do
5
+ lambda{ @var.this_method_does_not_exists }.should raise_error(NoMethodError)
6
+ end
7
+ end
8
+
9
+ describe Gecode::FreeBoolVar, '(not assigned)' do
10
+ before do
11
+ model = Gecode::Model.new
12
+ @var = model.bool_var
13
+ end
14
+
15
+ it_should_behave_like 'non-empty bool variable'
16
+
17
+ it 'should not be assigned' do
18
+ @var.should_not be_assigned
19
+ end
20
+ end
data/specs/branch.rb ADDED
@@ -0,0 +1,104 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class BranchSampleProblem < Gecode::Model
4
+ attr :vars
5
+ attr :bools
6
+
7
+ def initialize
8
+ @vars = int_var_array(2, 0..3)
9
+ @bools = bool_var_array(2)
10
+ end
11
+ end
12
+
13
+ describe Gecode::Model, ' (branch)' do
14
+ before do
15
+ @model = BranchSampleProblem.new
16
+ @vars = @model.vars
17
+ @bools = @model.bools
18
+ end
19
+
20
+ it 'should pass the variables given in int arrays' do
21
+ Gecode::Raw.should_receive(:branch).once.and_return{ |s, vars, x, y| vars }
22
+ int_var_array = @model.branch_on @vars
23
+ int_var_array.size.should equal(2)
24
+ 2.times do |i|
25
+ int_var_array.at(i).should have_domain(0..3)
26
+ end
27
+ end
28
+
29
+ it 'should pass the variables given in bool arrays' do
30
+ Gecode::Raw.should_receive(:branch).once.and_return{ |s, vars, x, y| vars }
31
+ bool_var_array = @model.branch_on @bools
32
+ bool_var_array.size.should equal(2)
33
+ end
34
+
35
+ it 'should default to :none and :min' do
36
+ Gecode::Raw.should_receive(:branch).once.with(@model.active_space,
37
+ anything, Gecode::Raw::BVAR_NONE, Gecode::Raw::BVAL_MIN)
38
+ @model.branch_on @vars
39
+ end
40
+
41
+ it 'should ensure that branched int variables are assigned in a solution' do
42
+ @model.branch_on @vars
43
+ @model.solve!.vars.each{ |var| var.should be_assigned }
44
+ end
45
+
46
+ it 'should ensure that branched bool variables are assigned in a solution' do
47
+ @model.branch_on @bools
48
+ @model.solve!.bools.each{ |var| var.should be_assigned }
49
+ end
50
+
51
+ supported_var_selectors = {
52
+ :none => Gecode::Raw::BVAR_NONE,
53
+ :smallest_min => Gecode::Raw::BVAR_MIN_MIN,
54
+ :largest_min => Gecode::Raw::BVAR_MIN_MAX,
55
+ :smallest_max => Gecode::Raw::BVAR_MAX_MIN,
56
+ :largest_max => Gecode::Raw::BVAR_MAX_MAX,
57
+ :smallest_size => Gecode::Raw::BVAR_SIZE_MIN,
58
+ :largest_size => Gecode::Raw::BVAR_SIZE_MAX,
59
+ :smallest_degree => Gecode::Raw::BVAR_DEGREE_MIN,
60
+ :largest_degree => Gecode::Raw::BVAR_DEGREE_MAX,
61
+ :smallest_min_regret => Gecode::Raw::BVAR_REGRET_MIN_MIN,
62
+ :largest_min_regret => Gecode::Raw::BVAR_REGRET_MIN_MAX,
63
+ :smallest_max_regret => Gecode::Raw::BVAR_REGRET_MAX_MIN,
64
+ :largest_max_regret => Gecode::Raw::BVAR_REGRET_MAX_MAX
65
+ }.each_pair do |name, gecode_const|
66
+ it "should support #{name} as variable selection strategy" do
67
+ Gecode::Raw.should_receive(:branch).once.with(@model.active_space,
68
+ anything, gecode_const, an_instance_of(Numeric))
69
+ @model.branch_on @vars, :variable => name
70
+ end
71
+ end
72
+
73
+ supported_val_selectors = {
74
+ :min => Gecode::Raw::BVAL_MIN,
75
+ :med => Gecode::Raw::BVAL_MED,
76
+ :max => Gecode::Raw::BVAL_MAX,
77
+ :split_min => Gecode::Raw::BVAL_SPLIT_MIN,
78
+ :split_max => Gecode::Raw::BVAL_SPLIT_MAX
79
+ }.each_pair do |name, gecode_const|
80
+ it "should support #{name} as value selection strategy" do
81
+ Gecode::Raw.should_receive(:branch).once.with(@model.active_space,
82
+ anything, an_instance_of(Numeric), gecode_const)
83
+ @model.branch_on @vars, :value => name
84
+ end
85
+ end
86
+
87
+ it 'should raise errors for unrecognized var selection strategies' do
88
+ lambda do
89
+ @model.branch_on @vars, :variable => :foo
90
+ end.should raise_error(ArgumentError)
91
+ end
92
+
93
+ it 'should raise errors for unrecognized val selection strategies' do
94
+ lambda do
95
+ @model.branch_on @vars, :value => :foo
96
+ end.should raise_error(ArgumentError)
97
+ end
98
+
99
+ it 'should raise errors for unrecognized options' do
100
+ lambda do
101
+ @model.branch_on @vars, :foo => 5
102
+ end.should raise_error(ArgumentError)
103
+ end
104
+ end