gecoder 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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