gecoder 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,13 @@
1
+ == Version 0.8.2
2
+ This release adds search statistics along with some new arithmetic constraints
3
+ and channel constraints between boolean and integer variables.
4
+
5
+ * Wrapping an enumerable that is already wrapped is no longer allowed.
6
+ * Added search statistics.
7
+ * Added squared value and square root constraints.
8
+ * Added channel constraints between single integer variables and boolean variables.
9
+ * Added channel constraints between enumerations of boolean variables and single integer variables.
10
+
1
11
  == Version 0.8.1
2
12
  This release adds tuple constraints along with a couple of minor features. It
3
13
  also fixes a bug introduced in the previous version.
@@ -908,6 +908,22 @@ Rust::Bindings::create_bindings Rust::Bindings::LangCxx, "gecode" do |b|
908
908
  func.add_parameter "Gecode::PropKind", "pk"
909
909
  end
910
910
 
911
+ ns.add_function "sqr" do |func|
912
+ func.add_parameter "Gecode::MSpace *", "home"
913
+ func.add_parameter "Gecode::IntVar", "x0"
914
+ func.add_parameter "Gecode::IntVar", "x1"
915
+ func.add_parameter "Gecode::IntConLevel", "icl"
916
+ func.add_parameter "Gecode::PropKind", "pk"
917
+ end
918
+
919
+ ns.add_function "sqrt" do |func|
920
+ func.add_parameter "Gecode::MSpace *", "home"
921
+ func.add_parameter "Gecode::IntVar", "x0"
922
+ func.add_parameter "Gecode::IntVar", "x1"
923
+ func.add_parameter "Gecode::IntConLevel", "icl"
924
+ func.add_parameter "Gecode::PropKind", "pk"
925
+ end
926
+
911
927
  ns.add_function "branch" do |func|
912
928
  func.add_parameter "Gecode::MSpace *", "home"
913
929
  func.add_parameter "Gecode::MIntVarArray *", "iva" do |param|
@@ -955,6 +971,25 @@ Rust::Bindings::create_bindings Rust::Bindings::LangCxx, "gecode" do |b|
955
971
  func.add_parameter "Gecode::PropKind", "pk"
956
972
  end
957
973
 
974
+ ns.add_function "channel" do |func|
975
+ func.add_parameter "Gecode::MSpace*", "home"
976
+ func.add_parameter "Gecode::IntVar", "x0"
977
+ func.add_parameter "Gecode::BoolVar", "x1"
978
+ func.add_parameter "Gecode::IntConLevel", "icl"
979
+ func.add_parameter "Gecode::PropKind", "pk"
980
+ end
981
+
982
+ ns.add_function "channel" do |func|
983
+ func.add_parameter "Gecode::MSpace*", "home"
984
+ func.add_parameter "Gecode::MBoolVarArray *", "x" do |param|
985
+ param.custom_conversion = "*ruby2Gecode_MBoolVarArrayPtr(argv[1], 2)->ptr()"
986
+ end
987
+ func.add_parameter "Gecode::IntVar", "y"
988
+ func.add_parameter "int", "o"
989
+ func.add_parameter "Gecode::IntConLevel", "icl"
990
+ func.add_parameter "Gecode::PropKind", "pk"
991
+ end
992
+
958
993
  ns.add_function "count" do |func|
959
994
  func.add_parameter "Gecode::MSpace*", "home"
960
995
  func.add_parameter "Gecode::MIntVarArray *", "x" do |param|
@@ -26,6 +26,13 @@ module Gecode
26
26
  # Describes a boolean expression (following after must*).
27
27
  class Expression #:nodoc:
28
28
  def ==(expression, options = {})
29
+ unless expression.kind_of?(ExpressionTree) or
30
+ expression.kind_of?(Gecode::FreeBoolVar) or
31
+ expression.kind_of?(TrueClass) or expression.kind_of?(FalseClass) or
32
+ expression.respond_to?(:to_minimodel_lin_exp)
33
+ raise TypeError, 'Invalid right hand side of boolean equation.'
34
+ end
35
+
29
36
  @params.update Gecode::Constraints::Util.decode_options(options)
30
37
  @model.add_constraint BooleanConstraint.new(@model,
31
38
  @params.update(:rhs => expression))
@@ -0,0 +1,23 @@
1
+ module Gecode::Constraints::Bool
2
+ class Expression
3
+ alias_method :pre_channel_equals, :==
4
+
5
+ # Constrains the boolean variable to be equal to the specified integer
6
+ # variable.
7
+ def ==(int, options = {})
8
+ unless @params[:lhs].kind_of?(Gecode::FreeBoolVar) and
9
+ int.kind_of?(Gecode::FreeIntVar)
10
+ return pre_channel_equals(int, options)
11
+ end
12
+
13
+ # Provide commutivity to the corresponding int variable constraint.
14
+ if @params[:negate]
15
+ int.must_not.equal(@params[:lhs], options)
16
+ else
17
+ int.must.equal(@params[:lhs], options)
18
+ end
19
+ end
20
+
21
+ alias_comparison_methods
22
+ end
23
+ end
@@ -0,0 +1,59 @@
1
+ module Gecode::Constraints::BoolEnum
2
+ class Expression
3
+ # Adds a channel constraint on the variables in the enum with the specified
4
+ # integer variable. Beyond the common options the channel constraint can
5
+ # also take the following option:
6
+ #
7
+ # [:offset] Specifies an offset for the integer variable. If the offset is
8
+ # set to k then the integer variable takes value i+k exactly
9
+ # when the variable at index i in the boolean enumration is true
10
+ # and the rest are false.
11
+ def channel(int_var, options = {})
12
+ if @params[:negate]
13
+ raise Gecode::MissingConstraintError, 'A negated channel constraint ' +
14
+ 'is not implemented.'
15
+ end
16
+ if options.has_key? :reify
17
+ raise ArgumentError, 'The channel constraint does not support the ' +
18
+ 'reification option.'
19
+ end
20
+ unless int_var.kind_of? Gecode::FreeIntVar
21
+ raise TypeError, "Expected an integer variable, got #{int_var.class}."
22
+ end
23
+
24
+ @params.update(:rhs => int_var, :offset => options.delete(:offset) || 0)
25
+ @params.update(Gecode::Constraints::Util.decode_options(options))
26
+ @model.add_constraint Channel::ChannelConstraint.new(@model, @params)
27
+ end
28
+ end
29
+
30
+ # A module that gathers the classes and modules used in channel constraints
31
+ # involving one boolean enum and one integer variable.
32
+ module Channel #:nodoc:
33
+ # Describes a channel constraint that "channels" an enumerations of
34
+ # boolean variables with an integer variable. This constrains the integer
35
+ # variable to take value i exactly when the variable at index i in the
36
+ # boolean enumeration is true and the others are false.
37
+ #
38
+ # Neither reification nor negation is supported.
39
+ #
40
+ # == Examples
41
+ #
42
+ # # Constrains the enumeration called +selected_option+ to be false in the
43
+ # # first four positions and have exactly one true variable in the other.
44
+ # selected_option.must.channel selected_option_index
45
+ # selected_option_index.must_be > 3
46
+ #
47
+ # # Constrains the enumeration called +selected_option+ to be false in the
48
+ # # first five positions and have exactly one true variable in the other.
49
+ # selected_option.must.channel(selected_option_index, :offset => 1)
50
+ # selected_option_index.must_be > 3
51
+ class ChannelConstraint < Gecode::Constraints::Constraint
52
+ def post
53
+ lhs, rhs, offset = @params.values_at(:lhs, :rhs, :offset)
54
+ Gecode::Raw::channel(@model.active_space, lhs.to_bool_var_array,
55
+ rhs.bind, offset, *propagation_options)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -10,16 +10,17 @@ module Gecode::Constraints::BoolEnum
10
10
  raise ArgumentError, 'Reification is not supported by the tuple ' +
11
11
  'constraint.'
12
12
  end
13
- unless tuples.respond_to?(:each) and
14
- tuples.all?{ |tuple| tuple.respond_to?(:each) }
15
- raise TypeError, 'Expected an enumeration with tuples, got ' +
16
- "#{tuples.class}."
17
- end
18
- unless tuples.all?{ |tuple|
19
- tuple.all?{ |x| x.kind_of?(TrueClass) or x.kind_of?(FalseClass) }}
20
- raise TypeError, 'All tuples must contain booleans.'
13
+
14
+ util = Gecode::Constraints::Util
15
+
16
+ # Check that the tuples are correct.
17
+ expected_size = @params[:lhs].size
18
+ util::Extensional.perform_tuple_checks(tuples, expected_size) do |tuple|
19
+ unless tuple.all?{ |x| x.kind_of?(TrueClass) or x.kind_of?(FalseClass) }
20
+ raise TypeError, 'All tuples must contain booleans.'
21
+ end
21
22
  end
22
-
23
+
23
24
  @params[:tuples] = tuples
24
25
  @model.add_constraint Extensional::TupleConstraint.new(@model,
25
26
  @params.update(Gecode::Constraints::Util.decode_options(options)))
@@ -30,3 +30,4 @@ end
30
30
 
31
31
  require 'gecoder/interface/constraints/bool_enum/relation'
32
32
  require 'gecoder/interface/constraints/bool_enum/extensional'
33
+ require 'gecoder/interface/constraints/bool_enum/channel'
@@ -73,3 +73,4 @@ end
73
73
 
74
74
  require 'gecoder/interface/constraints/bool/boolean'
75
75
  require 'gecoder/interface/constraints/bool/linear'
76
+ require 'gecoder/interface/constraints/bool/channel'
@@ -4,6 +4,20 @@ class Gecode::FreeIntVar
4
4
  Gecode::Constraints::Int::Arithmetic::AbsExpressionStub.new(@model,
5
5
  :lhs => self)
6
6
  end
7
+
8
+ # Initiates an arithmetic squared value constraint.
9
+ def squared
10
+ Gecode::Constraints::Int::Arithmetic::SquaredExpressionStub.new(@model,
11
+ :lhs => self)
12
+ end
13
+
14
+ # Initiates an arithmetic squared root constraint.
15
+ def sqrt
16
+ Gecode::Constraints::Int::Arithmetic::SquareRootExpressionStub.new(@model,
17
+ :lhs => self)
18
+ end
19
+ alias_method :square_root, :sqrt
20
+
7
21
 
8
22
  alias_method :pre_arith_mult, :* if instance_methods.include? '*'
9
23
 
@@ -52,7 +66,7 @@ module Gecode::Constraints::Int::Arithmetic #:nodoc:
52
66
  # # The value of +x*y+ must be equal to their sum.
53
67
  # (x*y).must == x + y
54
68
  #
55
- # # The valye of +x*y+ must be less than 17, with +bool+ as reification
69
+ # # The value of +x*y+ must be less than 17, with +bool+ as reification
56
70
  # # variable and +domain+ as strength.
57
71
  # (x*y).must_be.less_than(17, :reify => bool, :strength => :domain)
58
72
  class MultExpressionStub < Gecode::Constraints::Int::CompositeStub
@@ -69,4 +83,67 @@ module Gecode::Constraints::Int::Arithmetic #:nodoc:
69
83
  variable.bind, *propagation_options)
70
84
  end
71
85
  end
86
+
87
+ # Describes a CompositeStub for the squared constraint, which constrain
88
+ # the squared value of the variable.
89
+ #
90
+ # == Examples
91
+ #
92
+ # # The value of +x*x+ must be equal to y.
93
+ # x.squared.must == y
94
+ #
95
+ # # The value of +x*x+ must be less than or equal to 16, with +bool+ as
96
+ # # reification variable and +domain+ as strength.
97
+ # x.squared.must_be.less_or_equal(16, :reify => bool, :strength => :domain)
98
+ class SquaredExpressionStub < Gecode::Constraints::Int::CompositeStub
99
+ def constrain_equal(variable, params, constrain)
100
+ lhs = @params[:lhs]
101
+ if constrain
102
+ min = lhs.min; max = lhs.max
103
+ products = [min*max, min*min, max*max]
104
+ variable.must_be.in products.min..products.max
105
+ end
106
+
107
+ Gecode::Raw::sqr(@model.active_space, lhs.bind, variable.bind,
108
+ *propagation_options)
109
+ end
110
+ end
111
+
112
+ # Describes a CompositeStub for the square root constraint, which constrain
113
+ # the rounded down square root of the variable.
114
+ #
115
+ # == Examples
116
+ #
117
+ # # The square root of +x+, rounded down, must equal y.
118
+ # x.square_root.must == y
119
+ #
120
+ # # The square root of +x+, rounded down, must be larger than 17, with
121
+ # # +bool+ as reification variable and +domain+ as strength.
122
+ # x.square_root.must_be.larger_than(17, :reify => bool, :strength => :domain)
123
+ class SquareRootExpressionStub < Gecode::Constraints::Int::CompositeStub
124
+ def constrain_equal(variable, params, constrain)
125
+ lhs = @params[:lhs]
126
+ if constrain
127
+ max = lhs.max
128
+ if max < 0
129
+ # The left hand side does not have a real valued square root.
130
+ upper_bound = 0
131
+ else
132
+ upper_bound = Math.sqrt(max).floor;
133
+ end
134
+
135
+ min = lhs.min
136
+ if min < 0
137
+ lower_bound = 0
138
+ else
139
+ lower_bound = Math.sqrt(min).floor;
140
+ end
141
+
142
+ variable.must_be.in lower_bound..upper_bound
143
+ end
144
+
145
+ Gecode::Raw::sqrt(@model.active_space, lhs.bind, variable.bind,
146
+ *propagation_options)
147
+ end
148
+ end
72
149
  end
@@ -0,0 +1,73 @@
1
+ module Gecode::Constraints::Int
2
+ class Expression
3
+ alias_method :pre_channel_equals, :==
4
+
5
+ # Constrains the integer variable to be equal to the specified boolean
6
+ # variable. I.e. constrains the integer variable to be 1 when the boolean
7
+ # variable is true and 0 if the boolean variable is false.
8
+ def ==(bool, options = {})
9
+ unless @params[:lhs].kind_of?(Gecode::FreeIntVar) and
10
+ bool.kind_of?(Gecode::FreeBoolVar)
11
+ return pre_channel_equals(bool, options)
12
+ end
13
+
14
+ if @params[:negate]
15
+ raise Gecode::MissingConstraintError, 'A negated channel constraint ' +
16
+ 'is not implemented.'
17
+ end
18
+ unless options[:reify].nil?
19
+ raise ArgumentError, 'Reification is not supported by the channel ' +
20
+ 'constraint.'
21
+ end
22
+
23
+ @params.update(Gecode::Constraints::Util.decode_options(options))
24
+ @params[:rhs] = bool
25
+ @model.add_constraint Channel::ChannelConstraint.new(@model, @params)
26
+ end
27
+
28
+ alias_comparison_methods
29
+
30
+ # Adds a channel constraint on the integer variable and the variables in
31
+ # the specifed enum of boolean variables. Beyond the common options the
32
+ # channel constraint can also take the following option:
33
+ #
34
+ # [:offset] Specifies an offset for the integer variable. If the offset is
35
+ # set to k then the integer variable takes value i+k exactly
36
+ # when the variable at index i in the boolean enumration is true
37
+ # and the rest are false.
38
+ def channel(bool_enum, options = {})
39
+ unless bool_enum.respond_to? :to_bool_var_array
40
+ raise TypeError, 'Expected an enumration of boolean variables, got ' +
41
+ "#{bool_enum.class}."
42
+ end
43
+
44
+ # Just provide commutivity with the boolean enum channel constraint.
45
+ if @params[:negate]
46
+ bool_enum.must_not.channel(@params[:lhs], options)
47
+ else
48
+ bool_enum.must.channel(@params[:lhs], options)
49
+ end
50
+ end
51
+ end
52
+
53
+ # A module that gathers the classes and modules used in channel constraints
54
+ # involving a single integer variable.
55
+ module Channel #:nodoc:
56
+ # Describes a channel constraint that constrains an integer variable to be
57
+ # 1 if a boolean variable is true, and 0 when the boolean variable is false.
58
+ # Does not support negation nor reification.
59
+ #
60
+ # == Examples
61
+ #
62
+ # # The integer variable +x+ must be one exactly when the boolean
63
+ # # variable +bool+ is true.
64
+ # x.must == bool
65
+ class ChannelConstraint < Gecode::Constraints::Constraint
66
+ def post
67
+ lhs, rhs = @params.values_at(:lhs, :rhs)
68
+ Gecode::Raw::channel(@model.active_space, lhs.bind, rhs.bind,
69
+ *propagation_options)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -11,8 +11,16 @@ module Gecode::Constraints::IntEnum
11
11
  enum.respond_to?(:to_set_var_array)
12
12
  raise TypeError, "Expected int or set enum, got #{enum.class}."
13
13
  end
14
+ if enum.respond_to? :to_set_var_array
15
+ if options.has_key? :reify
16
+ raise ArgumentError, 'The channel constraints does not support the ' +
17
+ 'reification option.'
18
+ end
19
+ @params.update(Gecode::Constraints::Set::Util.decode_options(options))
20
+ else
21
+ @params.update(Gecode::Constraints::Util.decode_options(options))
22
+ end
14
23
 
15
- @params.update(Gecode::Constraints::Util.decode_options(options))
16
24
  @params.update(:rhs => enum)
17
25
  @model.add_constraint Channel::ChannelConstraint.new(@model, @params)
18
26
  end
@@ -91,7 +99,7 @@ module Gecode::Constraints::IntEnum
91
99
  Gecode::Raw::channel(@model.active_space, lhs, rhs.to_int_var_array,
92
100
  *propagation_options)
93
101
  else
94
- # Set var array, no strength.
102
+ # Set var array, no propagation options.
95
103
  Gecode::Raw::channel(@model.active_space, lhs, rhs.to_set_var_array)
96
104
  end
97
105
  end
@@ -1,6 +1,7 @@
1
1
  module Gecode::Constraints::IntEnum
2
2
  class Expression
3
- # Posts an equality constraint on the variables in the enum.
3
+ # Posts a tuple constraint on the variables in the enum, constraining them
4
+ # to equal one of the specified tuples.
4
5
  def in(tuples, options = {})
5
6
  if @params[:negate]
6
7
  raise Gecode::MissingConstraintError, 'A negated tuple constraint is ' +
@@ -10,18 +11,20 @@ module Gecode::Constraints::IntEnum
10
11
  raise ArgumentError, 'Reification is not supported by the tuple ' +
11
12
  'constraint.'
12
13
  end
13
- unless tuples.respond_to?(:each) and
14
- tuples.all?{ |tuple| tuple.respond_to?(:each) }
15
- raise TypeError, 'Expected an enumeration with tuples, got ' +
16
- "#{tuples.class}."
17
- end
18
- unless tuples.all?{ |tuple| tuple.all?{ |x| x.kind_of? Fixnum }}
19
- raise TypeError, 'All tuples must contain Fixnum.'
14
+
15
+ util = Gecode::Constraints::Util
16
+
17
+ # Check that the tuples are correct.
18
+ expected_size = @params[:lhs].size
19
+ util::Extensional.perform_tuple_checks(tuples, expected_size) do |tuple|
20
+ unless tuple.all?{ |x| x.kind_of? Fixnum }
21
+ raise TypeError, 'All tuples must contain Fixnum.'
22
+ end
20
23
  end
21
24
 
22
25
  @params[:tuples] = tuples
23
26
  @model.add_constraint Extensional::TupleConstraint.new(@model,
24
- @params.update(Gecode::Constraints::Util.decode_options(options)))
27
+ @params.update(util.decode_options(options)))
25
28
  end
26
29
  end
27
30
 
@@ -56,3 +56,4 @@ end
56
56
  require 'gecoder/interface/constraints/int/linear'
57
57
  require 'gecoder/interface/constraints/int/domain'
58
58
  require 'gecoder/interface/constraints/int/arithmetic'
59
+ require 'gecoder/interface/constraints/int/channel'
@@ -217,6 +217,40 @@ module Gecode
217
217
  end
218
218
  end
219
219
 
220
+ # A module that contains utility-methods for extensional constraints.
221
+ module Util::Extensional #:nodoc:
222
+ module_function
223
+
224
+ # Checks that the specified enumeration is an enumeration containing
225
+ # one or more tuples of the specified size. It also allows the caller
226
+ # to define additional tests by providing a block, which is given each
227
+ # tuple. If a test fails then an appropriate error is raised.
228
+ def perform_tuple_checks(tuples, expected_size, &additional_test)
229
+ unless tuples.respond_to?(:each)
230
+ raise TypeError, 'Expected an enumeration with tuples, got ' +
231
+ "#{tuples.class}."
232
+ end
233
+
234
+ if tuples.empty?
235
+ raise ArgumentError, 'One or more tuples must be specified.'
236
+ end
237
+
238
+ tuples.each do |tuple|
239
+ unless tuple.respond_to?(:each)
240
+ raise TypeError, 'Expected an enumeration containing enumeraions, ' +
241
+ "got #{tuple.class}."
242
+ end
243
+
244
+ unless tuple.size == expected_size
245
+ raise ArgumentError, 'All tuples must be of the same size as the ' +
246
+ 'number of variables in the array.'
247
+ end
248
+
249
+ yield tuple
250
+ end
251
+ end
252
+ end
253
+
220
254
  # Describes a constraint expressions. An expression is produced by calling
221
255
  # some form of must on a left hand side. The expression waits for a right
222
256
  # hand side so that it can post the corresponding constraint.
@@ -6,6 +6,9 @@ module Gecode
6
6
  unless enum.kind_of? Enumerable
7
7
  raise TypeError, 'Only enumerables can be wrapped.'
8
8
  end
9
+ if enum.kind_of? Gecode::EnumMethods
10
+ raise ArgumentError, 'The enumration is already wrapped.'
11
+ end
9
12
  elements = enum.to_a
10
13
  if elements.empty?
11
14
  raise ArgumentError, 'Enumerable must not be empty.'
@@ -4,7 +4,9 @@ module Gecode
4
4
  # to that solution. Returns the model if a solution was found, nil
5
5
  # otherwise.
6
6
  def solve!
7
- space = dfs_engine.next
7
+ dfs = dfs_engine
8
+ space = dfs.next
9
+ @statistics = dfs.statistics
8
10
  return nil if space.nil?
9
11
  self.active_space = space
10
12
  return self
@@ -14,6 +16,7 @@ module Gecode
14
16
  # might have been performed). Returns the reset model.
15
17
  def reset!
16
18
  self.active_space = base_space
19
+ @statistics = nil
17
20
  return self
18
21
  end
19
22
 
@@ -33,11 +36,33 @@ module Gecode
33
36
  next_solution = nil
34
37
  while not (next_solution = dfs.next).nil?
35
38
  self.active_space = next_solution
39
+ @statistics = dfs.statistics
36
40
  yield self
37
41
  end
38
42
  self.reset!
39
43
  end
40
44
 
45
+ # Returns search statistics providing various information from Gecode about
46
+ # the search that resulted in the model's current variable state. If the
47
+ # model's variables have not undegone any search then nil is returned. The
48
+ # statistics is a hash with the following keys:
49
+ # [:propagations] The number of propagation steps performed.
50
+ # [:failures] The number of failed nodes in the search tree.
51
+ # [:clones] The number of clones created.
52
+ # [:commits] The number of commit operations performed.
53
+ # [:memory] The peak memory allocated to Gecode.
54
+ def search_stats
55
+ return nil if @statistics.nil?
56
+
57
+ return {
58
+ :propagations => @statistics.propagate,
59
+ :failures => @statistics.fail,
60
+ :clones => @statistics.clone,
61
+ :commits => @statistics.commit,
62
+ :memory => @statistics.memory
63
+ }
64
+ end
65
+
41
66
  # Finds the optimal solution. Optimality is defined by the provided block
42
67
  # which is given one parameter, a solution to the problem. The block should
43
68
  # constrain the solution so that that only "better" solutions can be new
@@ -70,7 +95,14 @@ module Gecode
70
95
  options.c_d = Gecode::Raw::Search::Config::MINIMAL_DISTANCE
71
96
  options.a_d = Gecode::Raw::Search::Config::ADAPTIVE_DISTANCE
72
97
  options.stop = nil
73
- result = Gecode::Raw::bab(selected_space, options)
98
+ bab = Gecode::Raw::BAB.new(selected_space, options)
99
+
100
+ result = nil
101
+ previous_solution = nil
102
+ until (previous_solution = bab.next).nil?
103
+ result = previous_solution
104
+ end
105
+ @statistics = bab.statistics
74
106
 
75
107
  # Reset the method used constrain calls and return the result.
76
108
  Model.constrain_proc = nil
@@ -1,4 +1,4 @@
1
1
  module GecodeR
2
2
  # A string representation of the Gecode/R version.
3
- VERSION = '0.8.1'
3
+ VERSION = '0.8.2'
4
4
  end