gecoder 0.5.0 → 0.6.0

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