gecoder 0.5.0 → 0.6.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 (61) hide show
  1. data/CHANGES +16 -3
  2. data/example/magic_sequence.rb +1 -1
  3. data/example/queens.rb +1 -1
  4. data/example/send_more_money.rb +1 -1
  5. data/example/sudoku.rb +1 -1
  6. data/ext/missing.cpp +18 -4
  7. data/ext/missing.h +8 -0
  8. data/lib/gecoder/bindings.rb +30 -3
  9. data/lib/gecoder/bindings/bindings.rb +22 -0
  10. data/lib/gecoder/interface/binding_changes.rb +81 -107
  11. data/lib/gecoder/interface/branch.rb +65 -14
  12. data/lib/gecoder/interface/constraints.rb +1 -0
  13. data/lib/gecoder/interface/constraints/bool_enum/boolean.rb +16 -12
  14. data/lib/gecoder/interface/constraints/int/arithmetic.rb +7 -3
  15. data/lib/gecoder/interface/constraints/int/linear.rb +19 -16
  16. data/lib/gecoder/interface/constraints/int_enum/arithmetic.rb +8 -4
  17. data/lib/gecoder/interface/constraints/int_enum/channel.rb +14 -6
  18. data/lib/gecoder/interface/constraints/int_enum/element.rb +7 -5
  19. data/lib/gecoder/interface/constraints/int_enum/sort.rb +1 -4
  20. data/lib/gecoder/interface/constraints/set/cardinality.rb +6 -3
  21. data/lib/gecoder/interface/constraints/set/connection.rb +136 -0
  22. data/lib/gecoder/interface/constraints/set_enum/channel.rb +18 -0
  23. data/lib/gecoder/interface/constraints/set_enum/distinct.rb +61 -0
  24. data/lib/gecoder/interface/constraints/set_enum_constraints.rb +32 -0
  25. data/lib/gecoder/interface/constraints/set_var_constraints.rb +1 -0
  26. data/lib/gecoder/interface/enum_wrapper.rb +12 -3
  27. data/lib/gecoder/interface/model.rb +77 -56
  28. data/lib/gecoder/interface/search.rb +74 -5
  29. data/lib/gecoder/interface/variables.rb +117 -15
  30. data/lib/gecoder/version.rb +1 -1
  31. data/specs/binding_changes.rb +9 -5
  32. data/specs/bool_var.rb +8 -12
  33. data/specs/branch.rb +85 -19
  34. data/specs/constraints/arithmetic.rb +99 -71
  35. data/specs/constraints/bool_enum.rb +26 -18
  36. data/specs/constraints/boolean.rb +53 -49
  37. data/specs/constraints/cardinality.rb +33 -26
  38. data/specs/constraints/channel.rb +77 -6
  39. data/specs/constraints/connection.rb +352 -0
  40. data/specs/constraints/constraints.rb +10 -1
  41. data/specs/constraints/count.rb +79 -39
  42. data/specs/constraints/distinct.rb +128 -9
  43. data/specs/constraints/element.rb +26 -19
  44. data/specs/constraints/equality.rb +2 -1
  45. data/specs/constraints/int_domain.rb +19 -12
  46. data/specs/constraints/int_relation.rb +12 -6
  47. data/specs/constraints/linear.rb +30 -30
  48. data/specs/constraints/reification_sugar.rb +8 -4
  49. data/specs/constraints/set_domain.rb +24 -18
  50. data/specs/constraints/set_relation.rb +38 -23
  51. data/specs/constraints/sort.rb +12 -10
  52. data/specs/enum_wrapper.rb +9 -3
  53. data/specs/int_var.rb +8 -4
  54. data/specs/logging.rb +24 -0
  55. data/specs/model.rb +25 -7
  56. data/specs/search.rb +41 -1
  57. data/specs/set_var.rb +36 -7
  58. data/specs/spec_helper.rb +3 -10
  59. data/vendor/rust/rust/templates/FunctionDefinition.rusttpl +1 -1
  60. metadata +12 -3
  61. data/specs/tmp +0 -22
data/CHANGES CHANGED
@@ -1,3 +1,17 @@
1
+ == Version 0.6.0
2
+ This release adds most of the remaining set constraints. It also makes
3
+ backward-compatibility breaking changes to the way that properties of variables
4
+ are accessed.
5
+
6
+ * Added channel constraints involving set variables.
7
+ * Added set min and max constraints.
8
+ * Added set sum constraints (optionally weighted or substituted).
9
+ * Added include (match) constraint for sets.
10
+ * Added distinct constraints (distinct and at most one) for sets.
11
+ * Added branch and bound search (optimization search). It's quite shaky at the moment though.
12
+ * Assigned values of variables are now always accessed using #value instread of the various previous methods. The methods for extracting information from set variables have been changed and renamed.
13
+ * Enumerations containing variables now provide a convenience method #values which returns an array of the enum's values.
14
+
1
15
  == Version 0.5.0
2
16
  This release adds set variables and some of their constraints, along with the
3
17
  last of the boolean constraints.
@@ -10,10 +24,9 @@ last of the boolean constraints.
10
24
  * Added set cardinality constraints.
11
25
 
12
26
  == Version 0.4.0
13
- This release adds most of the constraints supported by Gecode for integer
14
- variables.
27
+ This release adds most of the integer variable constraints supported by Gecode.
15
28
 
16
- * [#11861] Fixed a bug stopping the creating of int variables with non-range domains.
29
+ * [#11861] Fixed a bug stopping the creation of int variables with non-range domains.
17
30
  * Added domain constraints for int variables.
18
31
  * Added equality constraint for int enums.
19
32
  * Matrices of integer and boolean variables can now be created using Model#int_var_matrix Model#bool_var_matrix.
@@ -29,7 +29,7 @@ class MagicSequence < Gecode::Model
29
29
  end
30
30
 
31
31
  def to_s
32
- @sequence.map{ |element| element.val }.join(', ')
32
+ @sequence.values.join(', ')
33
33
  end
34
34
  end
35
35
 
data/example/queens.rb CHANGED
@@ -25,7 +25,7 @@ class NQueens < Gecode::Model
25
25
 
26
26
  # Displays the assignment as a chessboard with queens denoted by 'x'.
27
27
  def to_s
28
- rows = @queen_rows.map{ |var| var.val }
28
+ rows = @queen_rows.values
29
29
 
30
30
  separator = '+' << '-' * (3 * @size + (@size - 1)) << "+\n"
31
31
  res = (0...@size).inject(separator) do |s, i|
@@ -25,7 +25,7 @@ class SendMoreMoney < Gecode::Model
25
25
 
26
26
  def to_s
27
27
  %w{s e n d m o r y}.zip(@letters).map do |text, letter|
28
- "#{text}: #{letter.val}"
28
+ "#{text}: #{letter.value}"
29
29
  end.join(', ')
30
30
  end
31
31
 
data/example/sudoku.rb CHANGED
@@ -44,7 +44,7 @@ class Sudoku < Gecode::Model
44
44
  separator = '+' << '-' * (3 * @size + (@size - 1)) << "+\n"
45
45
  res = (0...@size).inject(separator) do |s, i|
46
46
  (0...@size).inject(s + '|') do |s, j|
47
- s << " #{@squares[i,j].val} |"
47
+ s << " #{@squares[i,j].value} |"
48
48
  end << "\n" << separator
49
49
  end
50
50
  end
data/ext/missing.cpp CHANGED
@@ -18,6 +18,7 @@
18
18
  **/
19
19
 
20
20
  #include "missing.h"
21
+ #include "gecode.hh"
21
22
 
22
23
  #include <iostream>
23
24
  #include <map>
@@ -193,6 +194,14 @@ void MSpace::own(Gecode::MSetVarArray *sva, const char *name)
193
194
  d->setArrays[name] = sva;
194
195
  }
195
196
 
197
+ // For BAB.
198
+ void MSpace::constrain(MSpace* s)
199
+ {
200
+ // Call Ruby's constrain.
201
+ rb_funcall(Rust_gecode::cxx2ruby(this), rb_intern("constrain"), 1,
202
+ Rust_gecode::cxx2ruby(s));
203
+ }
204
+
196
205
  Gecode::MIntVarArray *MSpace::intVarArray(const char *name) const
197
206
  {
198
207
  if ( d->intArrays.find(name) == d->intArrays.end() ) return 0;
@@ -211,7 +220,6 @@ Gecode::MSetVarArray *MSpace::setVarArray(const char *name ) const
211
220
  return d->setArrays[name];
212
221
  }
213
222
 
214
-
215
223
  Gecode::MBranchingDesc *MSpace::mdescription()
216
224
  {
217
225
  if(!this->failed() || !d->description->ptr() )
@@ -237,6 +245,15 @@ MDFS::~MDFS()
237
245
  }
238
246
 
239
247
 
248
+ // BAB
249
+ MBAB::MBAB(MSpace *space, unsigned int c_d, unsigned int a_d, Search::Stop* st) : Gecode::BAB<MSpace>(space, c_d, a_d, st)
250
+ {
251
+ }
252
+
253
+ MBAB::~MBAB()
254
+ {
255
+ }
256
+
240
257
  namespace Search {
241
258
 
242
259
  // Stop
@@ -284,6 +301,3 @@ Gecode::Search::Stop* MStop::create(int fails, int time)
284
301
  }
285
302
 
286
303
  }
287
-
288
-
289
-
data/ext/missing.h CHANGED
@@ -61,6 +61,8 @@ class MSpace : public Space
61
61
  void own(Gecode::MBoolVarArray *bva, const char *name);
62
62
  void own(Gecode::MSetVarArray *sva, const char *name);
63
63
 
64
+ void constrain(MSpace* s);
65
+
64
66
  Gecode::MIntVarArray *intVarArray(const char *name ) const;
65
67
  Gecode::MBoolVarArray *boolVarArray(const char *name ) const;
66
68
  Gecode::MSetVarArray *setVarArray(const char *name) const;
@@ -80,6 +82,12 @@ class MDFS : public Gecode::Search::DFS
80
82
  MDFS(MSpace *space, unsigned int c_d, unsigned int a_d, Search::Stop* st = 0);
81
83
  ~MDFS();
82
84
  };
85
+ class MBAB : public Gecode::BAB<MSpace>
86
+ {
87
+ public:
88
+ MBAB(MSpace* space, unsigned int c_d, unsigned int a_d, Search::Stop* st = 0);
89
+ ~MBAB();
90
+ };
83
91
 
84
92
  namespace Search {
85
93
  class MStop : public Gecode::Search::Stop
@@ -1,7 +1,34 @@
1
1
  require 'gecode.so'
2
2
  module Gecode
3
- # The bindings are located in ::GecodeRaw, so we assign that to ::Gecode::Raw.
4
- # This is done because bindings/bindings.rb use ::GecodeRaw and appears to
5
- # have limitations that do not allow using a sub-namespace.
3
+ # The Gecode::Raw module is what the interface should use to access methods
4
+ # in Gecode. The actual bindings are located in ::GecodeRaw.
5
+
6
+ # We just make Gecode::Raw an alias of the real module.
6
7
  Raw = ::GecodeRaw
8
+ # Log all calls via Gecode::Raw.
9
+ #Raw = ::LoggingLayer
10
+
11
+ # Describes a layer that delegates to GecodeRaw only after having logged the
12
+ # call.
13
+ module LoggingLayer
14
+ require 'logger'
15
+
16
+ def self.method_missing(name, *args)
17
+ logger.info{ "#{name}(#{args.join(', ')})" }
18
+ ::GecodeRaw.send(name, *args)
19
+ end
20
+
21
+ def self.const_missing(name)
22
+ ::GecodeRaw.const_get(name)
23
+ end
24
+
25
+ # Gets the logger, or creates one if none exists.
26
+ def self.logger
27
+ return @logger unless @logger.nil?
28
+ file = open('gecoder.log', File::WRONLY | File::APPEND | File::CREAT)
29
+ @logger = ::Logger.new(file)
30
+ @logger.datetime_format = "%Y-%m-%d %H:%M:%S"
31
+ @logger
32
+ end
33
+ end
7
34
  end
@@ -293,6 +293,10 @@ Rust::Bindings::create_bindings Rust::Bindings::LangCxx, "gecode" do |b|
293
293
 
294
294
  klass.add_method "debug"
295
295
 
296
+ klass.add_method "constrain" do |method|
297
+ method.add_parameter "Gecode::MSpace*", "s"
298
+ end
299
+
296
300
  klass.add_method "own" do |method|
297
301
  method.add_parameter "Gecode::MIntVarArray *", "x"
298
302
  method.add_parameter "char*", "name"
@@ -390,6 +394,11 @@ Rust::Bindings::create_bindings Rust::Bindings::LangCxx, "gecode" do |b|
390
394
  method.add_parameter "int", "max"
391
395
  end
392
396
 
397
+ klass.add_constructor do |method|
398
+ method.add_parameter "Gecode::MSpace*", "home"
399
+ method.add_parameter "Gecode::IntSet", "s"
400
+ end
401
+
393
402
  klass.add_method "max", "int"
394
403
 
395
404
  klass.add_method "min", "int"
@@ -547,6 +556,19 @@ Rust::Bindings::create_bindings Rust::Bindings::LangCxx, "gecode" do |b|
547
556
  klass.add_method "statistics", "Gecode::Search::Statistics"
548
557
  end
549
558
 
559
+ ns.add_cxx_class "MBAB" do |klass|
560
+ klass.bindname = "BAB"
561
+ klass.add_constructor do |method|
562
+ method.add_parameter "Gecode::MSpace *", "s"
563
+ method.add_parameter "int", "c_d"
564
+ method.add_parameter "int", "a_d"
565
+ method.add_parameter "Gecode::Search::MStop *", "st"
566
+ end
567
+
568
+ klass.add_method "next", "Gecode::MSpace *"
569
+ klass.add_method "statistics", "Gecode::Search::Statistics"
570
+ end
571
+
550
572
  # SEARCH NAMESPACE
551
573
 
552
574
  ns.add_namespace "Search" do |searchns|
@@ -6,120 +6,83 @@
6
6
  #
7
7
  # This layer should be moved to the C++ side instead when possible for better
8
8
  # performance.
9
- module Gecode
10
- module Raw
11
- class Space
12
- # Creates the specified number of integer variables in the space. Returns
13
- # the indices with which they can then be accessed using int_var.
14
- def new_int_vars(min, max, count = 1)
15
- int_var_store.new_vars(min, max, count)
16
- end
17
-
18
- # Gets the int variable with the specified index, nil if none exists.
19
- def int_var(index)
20
- int_var_store[index]
21
- end
22
-
23
- # Creates the specified number of boolean variables in the space. Returns
24
- # the indices with which they can then be accessed using bool_var.
25
- def new_bool_vars(count = 1)
26
- bool_var_store.new_vars(count)
27
- end
28
-
29
- # Gets the bool variable with the specified index, nil if none exists.
30
- def bool_var(index)
31
- bool_var_store[index]
32
- end
33
-
34
- # Creates the specified number of set variables in the space with the
35
- # specified domain for greatest lower bound and least upper bound
36
- # (specified as either a range or enum). A range for the allowed
37
- # cardinality of the set can also be specified, if none is specified, or
38
- # nil is given, then the default range (anything) will be used. Returns
39
- # the indices with which they can then be accessed using set_var.
40
- def new_set_vars(*vars)
41
- set_var_store.new_vars(*vars)
42
- end
43
-
44
- # Gets the set variable with the specified index, nil if none exists.
45
- def set_var(index)
46
- set_var_store[index]
47
- end
48
-
49
- private
50
-
51
- # Retrieves the store used for integer variables. Creates one if none
52
- # exists.
53
- def int_var_store
54
- if @int_var_store.nil?
55
- @int_var_store = Gecode::Util::IntVarStore.new(self)
56
- end
57
- return @int_var_store
58
- end
59
-
60
- # Retrieves the store used for boolean variables. Creates one if none
61
- # exists.
62
- def bool_var_store
63
- if @bool_var_store.nil?
64
- @bool_var_store = Gecode::Util::BoolVarStore.new(self)
65
- end
66
- return @bool_var_store
67
- end
68
-
69
- # Retrieves the store used for set variables. Creates one if none exists.
70
- def set_var_store
71
- if @set_var_store.nil?
72
- @set_var_store = Gecode::Util::SetVarStore.new(self)
73
- end
74
- return @set_var_store
75
- end
9
+ module GecodeRaw
10
+ class Space
11
+ # Creates the specified number of integer variables in the space with the
12
+ # specified domain. Returns the indices with which they can then be
13
+ # accessed using int_var. The domain can be given as a Range (trated
14
+ # specially) or as another enum.
15
+ def new_int_vars(domain, count = 1)
16
+ int_var_store.new_vars(domain, count)
76
17
  end
77
18
 
78
- class IntVar
79
- # Aliases to make method-names more ruby-like.
80
- alias_method :assigned?, :assigned
81
- alias_method :in?, :in
82
- alias_method :include?, :in
83
- alias_method :range?, :range
19
+ # Gets the int variable with the specified index, nil if none exists.
20
+ def int_var(index)
21
+ int_var_store[index]
84
22
  end
85
23
 
86
- class BoolVar
87
- # Aliases to make method-names more ruby-like.
88
- alias_method :assigned?, :assigned
89
-
90
- def true?
91
- val == 1
24
+ # Creates the specified number of boolean variables in the space. Returns
25
+ # the indices with which they can then be accessed using bool_var.
26
+ def new_bool_vars(count = 1)
27
+ bool_var_store.new_vars(count)
28
+ end
29
+
30
+ # Gets the bool variable with the specified index, nil if none exists.
31
+ def bool_var(index)
32
+ bool_var_store[index]
33
+ end
34
+
35
+ # Creates the specified number of set variables in the space with the
36
+ # specified domain for greatest lower bound and least upper bound
37
+ # (specified as either a range or enum). A range for the allowed
38
+ # cardinality of the set can also be specified, if none is specified, or
39
+ # nil is given, then the default range (anything) will be used. Returns
40
+ # the indices with which they can then be accessed using set_var.
41
+ def new_set_vars(*vars)
42
+ set_var_store.new_vars(*vars)
43
+ end
44
+
45
+ # Gets the set variable with the specified index, nil if none exists.
46
+ def set_var(index)
47
+ set_var_store[index]
48
+ end
49
+
50
+ # Used by Gecode during BAB-search.
51
+ def constrain(best_so_far_space)
52
+ Gecode::Model.constrain(self, best_so_far_space)
53
+ end
54
+
55
+ private
56
+
57
+ # Retrieves the store used for integer variables. Creates one if none
58
+ # exists.
59
+ def int_var_store
60
+ if @int_var_store.nil?
61
+ @int_var_store = Gecode::Util::IntVarStore.new(self)
92
62
  end
93
-
94
- def false?
95
- val == 0
63
+ return @int_var_store
64
+ end
65
+
66
+ # Retrieves the store used for boolean variables. Creates one if none
67
+ # exists.
68
+ def bool_var_store
69
+ if @bool_var_store.nil?
70
+ @bool_var_store = Gecode::Util::BoolVarStore.new(self)
96
71
  end
72
+ return @bool_var_store
97
73
  end
98
74
 
99
- class SetVar
100
- # Aliases to make method-names more ruby-like.
101
- alias_method :assigned?, :assigned
102
-
103
- alias_method :include_glb?, :contains
104
- alias_method :include?, :contains
105
- def include_lub?(element)
106
- !notContains(element)
75
+ # Retrieves the store used for set variables. Creates one if none exists.
76
+ def set_var_store
77
+ if @set_var_store.nil?
78
+ @set_var_store = Gecode::Util::SetVarStore.new(self)
107
79
  end
108
-
109
- alias_method :glb_min, :glbMin
110
- alias_method :glb_max, :glbMax
111
- alias_method :lub_min, :lubMin
112
- alias_method :lub_max, :lubMax
113
-
114
- alias_method :glb_size, :glbSize
115
- alias_method :val_size, :glbSize
116
- alias_method :lub_size, :lubSize
117
-
118
- alias_method :card_min, :cardMin
119
- alias_method :card_max, :cardMax
80
+ return @set_var_store
120
81
  end
121
82
  end
122
-
83
+ end
84
+
85
+ module Gecode
123
86
  # Various utility (mainly used to change the behavior of the raw bindings).
124
87
  module Util
125
88
  # Provides common methods to the variable stores. The stores must provide
@@ -179,13 +142,24 @@ module Gecode
179
142
  @next_index = @size
180
143
  end
181
144
 
182
- # Creates the specified number of new int variables with the specified
183
- # range as domain. Returns the indices of the created variables as an array.
184
- def new_vars(min, max, count = 1)
145
+ # Creates the specified number of integer variables in the space with the
146
+ # specified domain. Returns the indices with which they can then be
147
+ # accessed using int_var. The domain can be given as a Range (trated
148
+ # specially) or as another enum.
149
+ def new_vars(domain, count = 1)
185
150
  grow(@next_index + count) # See the design note for more information.
186
151
  count.times do |i|
152
+ if domain.kind_of? Range
153
+ domain_params = [domain.first, domain.last]
154
+ elsif domain.kind_of? Enumerable
155
+ arr = domain.to_a
156
+ domain_params = [Gecode::Raw::IntSet.new(arr, arr.size)]
157
+ else
158
+ raise TypeError, "Expected Enumerable, got #{domain.class}."
159
+ end
160
+
187
161
  @var_array[@next_index] = Gecode::Raw::IntVar.new(@space,
188
- min, max)
162
+ *domain_params)
189
163
  @next_index += 1
190
164
  end
191
165
 
@@ -2,9 +2,9 @@ module Gecode
2
2
  class Model
3
3
  private
4
4
 
5
- # Maps the names of the supported variable branch strategies to the
6
- # corresponding constant in Gecode.
7
- BRANCH_VAR_CONSTANTS = {
5
+ # Maps the names of the supported variable branch strategies for integer and
6
+ # booleans to the corresponding constant in Gecode.
7
+ BRANCH_INT_VAR_CONSTANTS = {
8
8
  :none => Gecode::Raw::BVAR_NONE,
9
9
  :smallest_min => Gecode::Raw::BVAR_MIN_MIN,
10
10
  :largest_min => Gecode::Raw::BVAR_MIN_MAX,
@@ -19,16 +19,31 @@ module Gecode
19
19
  :smallest_max_regret => Gecode::Raw::BVAR_REGRET_MAX_MIN,
20
20
  :largest_max_regret => Gecode::Raw::BVAR_REGRET_MAX_MAX
21
21
  }
22
+ # Maps the names of the supported variable branch strategies for sets to
23
+ # the corresponding constant in Gecode.
24
+ BRANCH_SET_VAR_CONSTANTS = {
25
+ :none => Gecode::Raw::SETBVAR_NONE,
26
+ :smallest_cardinality => Gecode::Raw::SETBVAR_MIN_CARD,
27
+ :largest_cardinality => Gecode::Raw::SETBVAR_MAX_CARD,
28
+ :smallest_unknown => Gecode::Raw::SETBVAR_MIN_UNKNOWN_ELEM,
29
+ :largest_unknown => Gecode::Raw::SETBVAR_MAX_UNKNOWN_ELEM
30
+ }
22
31
 
23
- # Maps the names of the supported value branch strategies to the
24
- # corresponding constant in Gecode.
25
- BRANCH_VALUE_CONSTANTS = {
32
+ # Maps the names of the supported value branch strategies for integers and
33
+ # booleans to the corresponding constant in Gecode.
34
+ BRANCH_INT_VALUE_CONSTANTS = {
26
35
  :min => Gecode::Raw::BVAL_MIN,
27
36
  :med => Gecode::Raw::BVAL_MED,
28
37
  :max => Gecode::Raw::BVAL_MAX,
29
38
  :split_min => Gecode::Raw::BVAL_SPLIT_MIN,
30
39
  :split_max => Gecode::Raw::BVAL_SPLIT_MAX
31
40
  }
41
+ # Maps the names of the supported value branch strategies for sets to the
42
+ # corresponding constant in Gecode.
43
+ BRANCH_SET_VALUE_CONSTANTS = {
44
+ :min => Gecode::Raw::SETBVAL_MIN,
45
+ :max => Gecode::Raw::SETBVAL_MAX
46
+ }
32
47
 
33
48
  public
34
49
 
@@ -38,7 +53,8 @@ module Gecode
38
53
  # used with the :value option. If nothing is specified then :variable uses
39
54
  # :none and value uses :min.
40
55
  #
41
- # The following values can be used with :variable
56
+ # The following values can be used with :variable for integer and boolean
57
+ # enums:
42
58
  # [:none] The first unassigned variable.
43
59
  # [:smallest_min] The one with the smallest minimum.
44
60
  # [:largest_min] The one with the largest minimum.
@@ -71,32 +87,67 @@ module Gecode
71
87
  # the largest and second-largest value still in
72
88
  # the domain.
73
89
  #
74
- # The following values can be used with :value
90
+ # The following values can be used with :value for integer and boolean
91
+ # enums:
75
92
  # [:min] Selects the smallest value.
76
93
  # [:med] Select the median value.
77
94
  # [:max] Selects the largest vale
78
95
  # [:split_min] Selects the lower half of the domain.
79
96
  # [:split_max] Selects the upper half of the domain.
97
+ #
98
+ # The following values can be used with :variable for set enums:
99
+ # [:none] The first unassigned set.
100
+ # [:smallest_cardinality] The one with the smallest cardinality.
101
+ # [:largest_cardinality] The one with the largest cardinality.
102
+ # [:smallest_unknown] The one with the smallest number of unknown
103
+ # elements
104
+ # [:largest_unknown] The one with the largest number of unknown
105
+ # elements
106
+ #
107
+ # The following values can be used with :value set enums:
108
+ # enums:
109
+ # [:min] Selects the smallest value in the unknown part of the set.
110
+ # [:max] Selects the largest value in the unknown part of the set.
80
111
  def branch_on(variables, options = {})
112
+ if variables.respond_to? :to_int_var_array or
113
+ variables.respond_to? :to_bool_var_array
114
+ add_branch(variables, options, BRANCH_INT_VAR_CONSTANTS,
115
+ BRANCH_INT_VALUE_CONSTANTS)
116
+ elsif variables.respond_to? :to_set_var_array
117
+ add_branch(variables, options, BRANCH_SET_VAR_CONSTANTS,
118
+ BRANCH_SET_VALUE_CONSTANTS)
119
+ else
120
+ raise TypeError, "Unknown type of variable enum #{variables.class}."
121
+ end
122
+ end
123
+
124
+ private
125
+
126
+ # Adds a branching selection for the specified variable with the specified
127
+ # options. The hashes are used to decode the options into Gecode's
128
+ # constants.
129
+ def add_branch(variables, options, branch_var_hash, branch_value_hash)
81
130
  # Extract optional arguments.
82
131
  var_strat = options.delete(:variable) || :none
83
132
  val_strat = options.delete(:value) || :min
84
-
133
+
85
134
  # Check that the options are correct.
86
135
  unless options.empty?
87
136
  raise ArgumentError, 'Unknown branching option given: ' +
88
137
  options.keys.join(', ')
89
138
  end
90
- unless BRANCH_VAR_CONSTANTS.include? var_strat
139
+ unless branch_var_hash.include? var_strat
91
140
  raise ArgumentError, "Unknown variable selection strategy: #{var_strat}"
92
141
  end
93
- unless BRANCH_VALUE_CONSTANTS.include? val_strat
142
+ unless branch_value_hash.include? val_strat
94
143
  raise ArgumentError, "Unknown value selection strategy: #{val_strat}"
95
144
  end
96
145
 
97
- # Add the branching.
98
- Gecode::Raw.branch(active_space, variables.to_var_array,
99
- BRANCH_VAR_CONSTANTS[var_strat], BRANCH_VALUE_CONSTANTS[val_strat])
146
+ # Add the branching as a gecode interaction.
147
+ add_interaction do
148
+ Gecode::Raw.branch(active_space, variables.to_var_array,
149
+ branch_var_hash[var_strat], branch_value_hash[val_strat])
150
+ end
100
151
  end
101
152
  end
102
153
  end