gecoder 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,8 +1,18 @@
1
+ == Version 0.8.1
2
+ This release adds tuple constraints along with a couple of minor features. It
3
+ also fixes a bug introduced in the previous version.
4
+
5
+ * [#19435] Fixed a bug causing inconsistencies during BAB-search. The bug stopped the send+more=money example from working correctly.
6
+ * Fixed the "raw_bindings" and "sudoku-set" examples, which were broken by the 0.8.0 release.
7
+ * Integers can now be used to specify singleton lower and upper bounds when creating set variables.
8
+ * Added convenience methods Model#maximize! and Model#minimize! for optimizing single variables.
9
+ * Added tuple constraints for enumerations of integer and boolean variables.
10
+
1
11
  == Version 0.8.0
2
12
  This release makes the jump from using Gecode 1.3.1 to using Gecode 2.1.1 .
3
13
  The following changes have been made to the interface as a result of the jump.
4
14
 
5
- * The distinct constraint for sets has been removed.
15
+ * Removed the distinct constraint for sets.
6
16
  * Added the propagation kind option to the non-set constraints.
7
17
 
8
18
  == Version 0.7.1
data/Rakefile CHANGED
@@ -4,10 +4,11 @@ require 'rake/rdoctask'
4
4
  require 'rake/gempackagetask'
5
5
 
6
6
  require 'tasks/all_tasks'
7
- task :default => [:verify_rcov]
7
+ task :default => [:verify_rcov, :example_specs]
8
8
 
9
9
  desc 'Performs the tasks necessary when releasing'
10
- task :release => [:publish_website, :publish_packages, :tag]
10
+ task :release => [:clobber, :verify_rcov, :example_specs, :publish_website,
11
+ :publish_packages, :tag]
11
12
 
12
13
  desc 'Runs all the tests'
13
14
  task :test => :specs
@@ -12,14 +12,16 @@ s, e, n, d, m, o, r, y = (0..7).to_a.map{ |i| letters.at(i) }
12
12
  Gecode::Raw::post(space, (s * 1000 + e * 100 + n * 10 + d +
13
13
  m * 1000 + o * 100 + r * 10 + e).
14
14
  equal(m * 10000 + o * 1000 + n * 100 + e * 10 + y ),
15
- Gecode::Raw::ICL_DEF)
16
- Gecode::Raw::rel(space, s, Gecode::Raw::IRT_NQ, 0, Gecode::Raw::ICL_DEF)
17
- Gecode::Raw::rel(space, m, Gecode::Raw::IRT_NQ, 0, Gecode::Raw::ICL_DEF)
18
- Gecode::Raw::distinct(space, letters, Gecode::Raw::ICL_DEF)
15
+ Gecode::Raw::ICL_DEF, Gecode::Raw::PK_DEF)
16
+ Gecode::Raw::rel(space, s, Gecode::Raw::IRT_NQ, 0, Gecode::Raw::ICL_DEF,
17
+ Gecode::Raw::PK_DEF)
18
+ Gecode::Raw::rel(space, m, Gecode::Raw::IRT_NQ, 0, Gecode::Raw::ICL_DEF,
19
+ Gecode::Raw::PK_DEF)
20
+ Gecode::Raw::distinct(space, letters, Gecode::Raw::ICL_DEF, Gecode::Raw::PK_DEF)
19
21
 
20
22
  # Branching.
21
23
  Gecode::Raw::branch(space, letters,
22
- Gecode::Raw::BVAR_SIZE_MIN, Gecode::Raw::BVAL_MIN)
24
+ Gecode::Raw::INT_VAR_SIZE_MIN, Gecode::Raw::INT_VAL_MIN)
23
25
 
24
26
  # Search
25
27
  COPY_DIST = 16
@@ -61,9 +61,6 @@ class SudokuSet < Gecode::Model
61
61
  @sets[i].must_be.disjoint_with @sets[j]
62
62
  end
63
63
  end
64
- # The above implies that the sets must be distinct (since cardinality 0 is
65
- # not allowed), but we also explicitly add the distinctness constraint.
66
- @sets.must_be.distinct(:size => n)
67
64
 
68
65
  # The sets must intersect in exactly one element with each row column and
69
66
  # block. I.e. an assignable number must be assigned exactly once in each
@@ -149,7 +149,7 @@ Rust::Bindings::create_bindings Rust::Bindings::LangCxx, "gecode" do |b|
149
149
  enum.add_value "SRT_CMPL"
150
150
  end
151
151
 
152
- ns.add_enum "SetOpType " do |enum|
152
+ ns.add_enum "SetOpType" do |enum|
153
153
  enum.add_value "SOT_UNION"
154
154
  enum.add_value "SOT_DUNION"
155
155
  enum.add_value "SOT_INTER"
@@ -337,6 +337,20 @@ Rust::Bindings::create_bindings Rust::Bindings::LangCxx, "gecode" do |b|
337
337
  klass.add_method "debug"
338
338
  end
339
339
 
340
+ ns.add_cxx_class "TupleSet" do |klass|
341
+ klass.add_constructor
342
+
343
+ klass.add_method "add" do |method|
344
+ method.add_parameter "Gecode::IntArgs", "tuple"
345
+ end
346
+
347
+ klass.add_method "finalize"
348
+
349
+ klass.add_method "finalized", "bool"
350
+
351
+ klass.add_method "tuples", "int"
352
+ end
353
+
340
354
  ns.add_cxx_class "MBranchingDesc" do |klass|
341
355
  klass.bindname = "BranchingDesc"
342
356
  klass.add_constructor
@@ -1207,6 +1221,26 @@ Rust::Bindings::create_bindings Rust::Bindings::LangCxx, "gecode" do |b|
1207
1221
  func.add_parameter "Gecode::PropKind", "pk"
1208
1222
  end
1209
1223
 
1224
+ ns.add_function "extensional", "void" do |func|
1225
+ func.add_parameter "Gecode::MSpace*", "home"
1226
+ func.add_parameter "Gecode::MIntVarArray *", "x" do |param|
1227
+ param.custom_conversion = "*ruby2Gecode_MIntVarArrayPtr(argv[1], 2)->ptr()"
1228
+ end
1229
+ func.add_parameter "Gecode::TupleSet", "t"
1230
+ func.add_parameter "Gecode::IntConLevel", "icl"
1231
+ func.add_parameter "Gecode::PropKind", "pk"
1232
+ end
1233
+
1234
+ ns.add_function "extensional", "void" do |func|
1235
+ func.add_parameter "Gecode::MSpace*", "home"
1236
+ func.add_parameter "Gecode::MBoolVarArray *", "x" do |param|
1237
+ param.custom_conversion = "*ruby2Gecode_MBoolVarArrayPtr(argv[1], 2)->ptr()"
1238
+ end
1239
+ func.add_parameter "Gecode::TupleSet", "t"
1240
+ func.add_parameter "Gecode::IntConLevel", "icl"
1241
+ func.add_parameter "Gecode::PropKind", "pk"
1242
+ end
1243
+
1210
1244
  # ns.add_function "regular", "void" do |func|
1211
1245
  # func.add_parameter "Gecode::MSpace*", "home"
1212
1246
  # func.add_parameter "Gecode::MIntVarArray *", "x" do |param|
@@ -291,15 +291,11 @@ module Gecode
291
291
 
292
292
  private
293
293
 
294
- # Transforms a lub or glb domain given as a range or enumeration into one
295
- # or more parameters that describe the domain to Gecode::Raw::SetVar .
294
+ # Transforms a lub or glb domain given as a fixnum, range or enumeration
295
+ # into one or more parameters that describe the domain to
296
+ # Gecode::Raw::SetVar .
296
297
  def domain_to_args(domain)
297
- if domain.kind_of? Range
298
- return domain.first, domain.last
299
- else
300
- elements = domain.to_a
301
- return Gecode::Raw::IntSet.new(domain, domain.size)
302
- end
298
+ Gecode::Constraints::Util.constant_set_to_int_set(domain)
303
299
  end
304
300
 
305
301
  # Creates a new storage array for bool variables.
@@ -0,0 +1,63 @@
1
+ module Gecode::Constraints::BoolEnum
2
+ class Expression
3
+ # Posts an equality constraint on the variables in the enum.
4
+ def in(tuples, options = {})
5
+ if @params[:negate]
6
+ raise Gecode::MissingConstraintError, 'A negated tuple constraint is ' +
7
+ 'not implemented.'
8
+ end
9
+ unless options[:reify].nil?
10
+ raise ArgumentError, 'Reification is not supported by the tuple ' +
11
+ 'constraint.'
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.'
21
+ end
22
+
23
+ @params[:tuples] = tuples
24
+ @model.add_constraint Extensional::TupleConstraint.new(@model,
25
+ @params.update(Gecode::Constraints::Util.decode_options(options)))
26
+ end
27
+ end
28
+
29
+ # A module that gathers the classes and modules used in extensional
30
+ # constraints.
31
+ module Extensional #:nodoc:
32
+ # Describes a tuple constraint, which constrains the variables in an
33
+ # boolean enumeration to be equal to one of the specified tuples. Neither
34
+ # negation nor reification is supported.
35
+ #
36
+ # == Example
37
+ #
38
+ # # Constrains the three boolean variables in +bools+ to either
39
+ # # be true, false, true, or false, false, true.
40
+ # bools.must_be.in [[true, false, true], [false, false, true]]
41
+ #
42
+ # # The same as above, but preferring speed over low memory usage.
43
+ # bools.must_be.in([[true, false, true], [false, false, true]],
44
+ # :kind => :speed)
45
+ class TupleConstraint < Gecode::Constraints::Constraint
46
+ def post
47
+ # Bind lhs.
48
+ lhs = @params[:lhs].to_bool_var_array
49
+
50
+ # Create the tuple set.
51
+ tuple_set = Gecode::Raw::TupleSet.new
52
+ @params[:tuples].each do |tuple|
53
+ tuple_set.add tuple.map{ |b| b ? 1 : 0 }
54
+ end
55
+ tuple_set.finalize
56
+
57
+ # Post the constraint.
58
+ Gecode::Raw::extensional(@model.active_space, lhs, tuple_set,
59
+ *propagation_options)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -3,21 +3,23 @@ module Gecode
3
3
  # Produces an expression that can be handled as if it was a variable
4
4
  # representing the conjunction of all boolean variables in the enumeration.
5
5
  def conjunction
6
- return Gecode::Constraints::BoolEnum::ConjunctionStub.new(
6
+ return Gecode::Constraints::BoolEnum::Relation::ConjunctionStub.new(
7
7
  @model, :lhs => self)
8
8
  end
9
9
 
10
10
  # Produces an expression that can be handled as if it was a variable
11
11
  # representing the disjunction of all boolean variables in the enumeration.
12
12
  def disjunction
13
- return Gecode::Constraints::BoolEnum::DisjunctionStub.new(
13
+ return Gecode::Constraints::BoolEnum::Relation::DisjunctionStub.new(
14
14
  @model, :lhs => self)
15
15
  end
16
16
  end
17
-
17
+ end
18
+
19
+ module Gecode::Constraints::BoolEnum
18
20
  # A module that gathers the classes and modules used by boolean enumeration
19
- # constraints.
20
- module Constraints::BoolEnum
21
+ # relation constraints.
22
+ module Relation #:nodoc:
21
23
  # Describes a CompositeStub for the conjunction constraint, which constrain
22
24
  # the conjunction of all boolean variables in an enumeration.
23
25
  #
@@ -83,4 +85,4 @@ module Gecode
83
85
  end
84
86
  end
85
87
  end
86
- end
88
+ end
@@ -1,8 +1,32 @@
1
- module Gecode
1
+ module Gecode
2
+ module BoolEnumMethods
3
+ include Gecode::Constraints::LeftHandSideMethods
4
+
5
+ private
6
+
7
+ # Produces an expression for the lhs module.
8
+ def expression(params)
9
+ params.update(:lhs => self)
10
+ Constraints::BoolEnum::Expression.new(@model, params)
11
+ end
12
+ end
13
+
2
14
  # A module containing constraints that have enumerations of boolean variables
3
15
  # as left hand side.
4
16
  module Constraints::BoolEnum
17
+ # Expressions with bool enums as left hand sides.
18
+ class Expression < Gecode::Constraints::Expression #:nodoc:
19
+ # Raises TypeError unless the left hand side is a bool enum.
20
+ def initialize(model, params)
21
+ super
22
+
23
+ unless params[:lhs].respond_to? :to_bool_var_array
24
+ raise TypeError, 'Must have bool enum as left hand side.'
25
+ end
26
+ end
27
+ end
5
28
  end
6
29
  end
7
30
 
8
- require 'gecoder/interface/constraints/bool_enum/boolean'
31
+ require 'gecoder/interface/constraints/bool_enum/relation'
32
+ require 'gecoder/interface/constraints/bool_enum/extensional'
@@ -0,0 +1,61 @@
1
+ module Gecode::Constraints::IntEnum
2
+ class Expression
3
+ # Posts an equality constraint on the variables in the enum.
4
+ def in(tuples, options = {})
5
+ if @params[:negate]
6
+ raise Gecode::MissingConstraintError, 'A negated tuple constraint is ' +
7
+ 'not implemented.'
8
+ end
9
+ unless options[:reify].nil?
10
+ raise ArgumentError, 'Reification is not supported by the tuple ' +
11
+ 'constraint.'
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| tuple.all?{ |x| x.kind_of? Fixnum }}
19
+ raise TypeError, 'All tuples must contain Fixnum.'
20
+ end
21
+
22
+ @params[:tuples] = tuples
23
+ @model.add_constraint Extensional::TupleConstraint.new(@model,
24
+ @params.update(Gecode::Constraints::Util.decode_options(options)))
25
+ end
26
+ end
27
+
28
+ # A module that gathers the classes and modules used in extensional
29
+ # constraints.
30
+ module Extensional #:nodoc:
31
+ # Describes a tuple constraint, which constrains all the variables in an
32
+ # enumeration of integer variables to be equal to one of the specified
33
+ # tuples. Neither negation nor reification is supported.
34
+ #
35
+ # == Example
36
+ #
37
+ # # Constrains the two integer variables in +numbers+ to either have
38
+ # # values 1 and 7, or values 47 and 11.
39
+ # numbers.must_be.in [[1,7], [47,11]]
40
+ #
41
+ # # The same as above, but preferring speed over low memory usage.
42
+ # numbers.must_be.in([[1,7], [47,11]], :kind => :speed)
43
+ class TupleConstraint < Gecode::Constraints::Constraint
44
+ def post
45
+ # Bind lhs.
46
+ lhs = @params[:lhs].to_int_var_array
47
+
48
+ # Create the tuple set.
49
+ tuple_set = Gecode::Raw::TupleSet.new
50
+ @params[:tuples].each do |tuple|
51
+ tuple_set.add tuple
52
+ end
53
+ tuple_set.finalize
54
+
55
+ # Post the constraint.
56
+ Gecode::Raw::extensional(@model.active_space, lhs, tuple_set,
57
+ *propagation_options)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -35,3 +35,4 @@ require 'gecoder/interface/constraints/int_enum/element'
35
35
  require 'gecoder/interface/constraints/int_enum/count'
36
36
  require 'gecoder/interface/constraints/int_enum/sort'
37
37
  require 'gecoder/interface/constraints/int_enum/arithmetic'
38
+ require 'gecoder/interface/constraints/int_enum/extensional'
@@ -67,11 +67,11 @@ module Gecode
67
67
  end
68
68
 
69
69
  # Creates a set variable with the specified domain for greatest lower bound
70
- # and least upper bound (specified as either a range or enum). If no bounds
71
- # are specified then the empty set is used as greates lower bound and the
72
- # universe as least upper bound. A range for the allowed cardinality of the
73
- # set can also be specified, if none is specified, or nil is given, then the
74
- # default range (anything) will be used. If only a single Fixnum is
70
+ # and least upper bound (specified as either a fixnum, range or enum). If
71
+ # no bounds are specified then the empty set is used as greates lower bound
72
+ # and the universe as least upper bound. A range for the allowed cardinality
73
+ # of the set can also be specified, if none is specified, or nil is given,
74
+ # then the default range (anything) will be used. If only a single Fixnum is
75
75
  # specified as cardinality_range then it's used as lower bound.
76
76
  def set_var(glb_domain = [], lub_domain =
77
77
  Gecode::Raw::SetLimits::MIN..Gecode::Raw::SetLimits::MAX,
@@ -220,6 +220,8 @@ module Gecode
220
220
  if glb.kind_of?(Range) and lub.kind_of?(Range)
221
221
  glb.first >= lub.first and glb.last <= lub.last
222
222
  else
223
+ glb = [glb] if glb.kind_of?(Fixnum)
224
+ lub = [lub] if lub.kind_of?(Fixnum)
223
225
  (glb.to_a - lub.to_a).empty?
224
226
  end
225
227
  end
@@ -232,7 +234,8 @@ module Gecode
232
234
  # Retrieves the currently selected space, the one which constraints and
233
235
  # variables should be bound to.
234
236
  def selected_space
235
- @active_space ||= base_space
237
+ return @active_space unless @active_space.nil?
238
+ self.active_space = base_space
236
239
  end
237
240
 
238
241
  # Retrieves the space that should be used for variable creation.
@@ -243,9 +246,28 @@ module Gecode
243
246
  # Refreshes all cached variables. This should be called if the variables
244
247
  # in an existing space were changed.
245
248
  def refresh_variables
249
+ return if @variables.nil?
246
250
  @variables.each do |variable|
247
251
  variable.refresh if variable.cached?
248
252
  end
249
253
  end
254
+
255
+ # Executes any interactions with Gecode still waiting in the queue
256
+ # (emptying the queue) in the process.
257
+ def perform_queued_gecode_interactions
258
+ allow_space_access do
259
+ gecode_interaction_queue.each{ |con| con.call }
260
+ gecode_interaction_queue.clear # Empty the queue.
261
+ end
262
+ end
263
+
264
+ # Switches the active space used (the space from which variables are read
265
+ # and to which constraints are posted). @active_space should never be
266
+ # assigned directly.
267
+ def active_space=(new_space)
268
+ @active_space = new_space
269
+ new_space.refresh
270
+ refresh_variables
271
+ end
250
272
  end
251
273
  end
@@ -6,14 +6,14 @@ module Gecode
6
6
  def solve!
7
7
  space = dfs_engine.next
8
8
  return nil if space.nil?
9
- @active_space = space
9
+ self.active_space = space
10
10
  return self
11
11
  end
12
12
 
13
13
  # Returns to the original state, before any search was made (but propagation
14
14
  # might have been performed). Returns the reset model.
15
15
  def reset!
16
- @active_space = base_space
16
+ self.active_space = base_space
17
17
  return self
18
18
  end
19
19
 
@@ -30,7 +30,9 @@ module Gecode
30
30
  # Yields each solution that the model has.
31
31
  def each_solution(&block)
32
32
  dfs = dfs_engine
33
- while not (@active_space = dfs.next).nil?
33
+ next_solution = nil
34
+ while not (next_solution = dfs.next).nil?
35
+ self.active_space = next_solution
34
36
  yield self
35
37
  end
36
38
  self.reset!
@@ -54,10 +56,10 @@ module Gecode
54
56
 
55
57
  # Set the method used for constrain calls by the BAB-search.
56
58
  Model.constrain_proc = lambda do |home_space, best_space|
57
- @active_space = best_space
59
+ self.active_space = best_space
58
60
  @variable_creation_space = home_space
59
61
  yield(self, self)
60
- @active_space = home_space
62
+ self.active_space = home_space
61
63
  @variable_creation_space = nil
62
64
 
63
65
  perform_queued_gecode_interactions
@@ -74,13 +76,49 @@ module Gecode
74
76
  Model.constrain_proc = nil
75
77
  return nil if result.nil?
76
78
 
77
- # Refresh the solution.
78
- result.refresh
79
- refresh_variables
80
- @active_space = result
79
+ # Switch to the result.
80
+ self.active_space = result
81
81
  return self
82
82
  end
83
83
 
84
+ # Finds the solution that maximizes a given integer variable. The name of
85
+ # the method that accesses the variable from the model should be given. To
86
+ # for instance maximize a variable named "profit", that's accessible through
87
+ # the model, one would use the following.
88
+ #
89
+ # model.maximize! :profit
90
+ #
91
+ # Returns nil if there is no solution.
92
+ def maximize!(var)
93
+ variable = self.method(var).call
94
+ unless variable.kind_of? Gecode::FreeIntVar
95
+ raise ArgumentError.new("Expected integer variable, got #{variable.class}.")
96
+ end
97
+
98
+ optimize! do |model, best_so_far|
99
+ model.method(var).call.must > best_so_far.method(var).call.value
100
+ end
101
+ end
102
+
103
+ # Finds the solution that minimizes a given integer variable. The name of
104
+ # the method that accesses the variable from the model should be given. To
105
+ # for instance minimize a variable named "cost", that's accessible through
106
+ # the model, one would use the following.
107
+ #
108
+ # model.minimize! :cost
109
+ #
110
+ # Returns nil if there is no solution.
111
+ def minimize!(var)
112
+ variable = self.method(var).call
113
+ unless variable.kind_of? Gecode::FreeIntVar
114
+ raise ArgumentError.new("Expected integer variable, got #{variable.class}.")
115
+ end
116
+
117
+ optimize! do |model, best_so_far|
118
+ model.method(var).call.must < best_so_far.method(var).call.value
119
+ end
120
+ end
121
+
84
122
  class <<self
85
123
  # Sets the proc that should be used to handle constrain requests.
86
124
  def constrain_proc=(proc) #:nodoc:
@@ -111,14 +149,5 @@ module Gecode
111
149
  Gecode::Raw::Search::Config::ADAPTIVE_DISTANCE,
112
150
  nil)
113
151
  end
114
-
115
- # Executes any interactions with Gecode still waiting in the queue
116
- # (emptying the queue) in the process.
117
- def perform_queued_gecode_interactions
118
- allow_space_access do
119
- gecode_interaction_queue.each{ |con| con.call }
120
- gecode_interaction_queue.clear # Empty the queue.
121
- end
122
- end
123
152
  end
124
153
  end
@@ -1,4 +1,4 @@
1
1
  module GecodeR
2
2
  # A string representation of the Gecode/R version.
3
- VERSION = '0.8.0'
3
+ VERSION = '0.8.1'
4
4
  end
@@ -17,7 +17,7 @@ end
17
17
 
18
18
  # Expects @stub, which contains the started constraint and @compute_result which
19
19
  # computes whether the left hand side is true or not.
20
- describe 'bool enum constraint', :shared => true do
20
+ describe 'bool enum relation constraint', :shared => true do
21
21
  it 'should handle being constrained to be true' do
22
22
  @stub.must_be.true
23
23
  @model.solve!
@@ -67,7 +67,7 @@ describe 'bool enum constraint', :shared => true do
67
67
  end
68
68
  end
69
69
 
70
- describe Gecode::Constraints::BoolEnum, ' (conjunction)' do
70
+ describe Gecode::Constraints::BoolEnum::Relation, ' (conjunction)' do
71
71
  before do
72
72
  @model = BoolEnumSampleProblem.new
73
73
  @bools = @model.bools
@@ -109,11 +109,11 @@ describe Gecode::Constraints::BoolEnum, ' (conjunction)' do
109
109
  @compute_result = lambda{ @bools.all?{ |b| b.value } }
110
110
  end
111
111
 
112
- it_should_behave_like 'bool enum constraint'
112
+ it_should_behave_like 'bool enum relation constraint'
113
113
  it_should_behave_like 'reifiable constraint'
114
114
  end
115
115
 
116
- describe Gecode::Constraints::BoolEnum, ' (disjunction)' do
116
+ describe Gecode::Constraints::BoolEnum::Relation, ' (disjunction)' do
117
117
  before do
118
118
  @model = BoolEnumSampleProblem.new
119
119
  @bools = @model.bools
@@ -155,6 +155,6 @@ describe Gecode::Constraints::BoolEnum, ' (disjunction)' do
155
155
  @compute_result = lambda{ @bools.any?{ |b| b.value } }
156
156
  end
157
157
 
158
- it_should_behave_like 'bool enum constraint'
158
+ it_should_behave_like 'bool enum relation constraint'
159
159
  it_should_behave_like 'reifiable constraint'
160
160
  end
@@ -17,6 +17,15 @@ describe Gecode::Constraints::IntEnum::Expression do
17
17
  end
18
18
  end
19
19
 
20
+ describe Gecode::Constraints::BoolEnum::Expression do
21
+ it 'should raise error unless lhs is a bool enum' do
22
+ lambda do
23
+ Gecode::Constraints::BoolEnum::Expression.new(Gecode::Model.new,
24
+ :lhs => 'foo', :negate => false)
25
+ end.should raise_error(TypeError)
26
+ end
27
+ end
28
+
20
29
  describe Gecode::Constraints::SetEnum::Expression do
21
30
  it 'should raise error unless lhs is a set enum' do
22
31
  lambda do
@@ -0,0 +1,106 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/constraint_helper'
3
+
4
+ describe Gecode::Constraints::IntEnum::Extensional do
5
+ before do
6
+ @model = Gecode::Model.new
7
+ @tuples = [[1,7], [5,1]]
8
+ @digits = @model.int_var_array(2, 0..9)
9
+ @model.branch_on @digits
10
+
11
+ @invoke_options = lambda do |hash|
12
+ @digits.must_be.in(@tuples, hash)
13
+ @model.solve!
14
+ end
15
+ @expect_options = option_expectation do |strength, kind, reif_var|
16
+ Gecode::Raw.should_receive(:extensional).once.with(
17
+ an_instance_of(Gecode::Raw::Space),
18
+ an_instance_of(Gecode::Raw::IntVarArray),
19
+ an_instance_of(Gecode::Raw::TupleSet), strength, kind)
20
+ end
21
+ end
22
+
23
+ it 'should constrain the domain of all variables' do
24
+ @digits.must_be.in @tuples
25
+
26
+ found_solutions = []
27
+ @model.each_solution do |m|
28
+ found_solutions << @digits.values
29
+ end
30
+
31
+ found_solutions.size.should == 2
32
+ (found_solutions - @tuples).should be_empty
33
+ end
34
+
35
+ it 'should not allow negation' do
36
+ lambda do
37
+ @digits.must_not_be.in @tuples
38
+ end.should raise_error(Gecode::MissingConstraintError)
39
+ end
40
+
41
+ it 'should raise error if the right hand side is not an enumeration' do
42
+ lambda{ @digits.must_be.in 4711 }.should raise_error(TypeError)
43
+ end
44
+
45
+ it 'should raise error if the right hand side does not contain tuples' do
46
+ lambda{ @digits.must_be.in [17, 4711] }.should raise_error(TypeError)
47
+ end
48
+
49
+ it 'should raise error if the right hand side does not contain integer tuples' do
50
+ lambda{ @digits.must_be.in ['hello'] }.should raise_error(TypeError)
51
+ end
52
+
53
+ it_should_behave_like 'non-reifiable constraint'
54
+ end
55
+
56
+ describe Gecode::Constraints::BoolEnum::Extensional do
57
+ before do
58
+ @model = Gecode::Model.new
59
+ @tuples = [[true, false, true], [false, false, true]]
60
+ @bools = @model.bool_var_array(3)
61
+ @model.branch_on @bools
62
+
63
+ @invoke_options = lambda do |hash|
64
+ @bools.must_be.in(@tuples, hash)
65
+ @model.solve!
66
+ end
67
+ @expect_options = option_expectation do |strength, kind, reif_var|
68
+ Gecode::Raw.should_receive(:extensional).once.with(
69
+ an_instance_of(Gecode::Raw::Space),
70
+ an_instance_of(Gecode::Raw::BoolVarArray),
71
+ an_instance_of(Gecode::Raw::TupleSet), strength, kind)
72
+ end
73
+ end
74
+
75
+ it 'should constrain the domain of all variables' do
76
+ @bools.must_be.in @tuples
77
+
78
+ found_solutions = []
79
+ @model.each_solution do |m|
80
+ found_solutions << @bools.values
81
+ end
82
+
83
+ found_solutions.size.should == 2
84
+ (found_solutions - @tuples).should be_empty
85
+ end
86
+
87
+ it 'should not allow negation' do
88
+ lambda do
89
+ @bools.must_not_be.in @tuples
90
+ end.should raise_error(Gecode::MissingConstraintError)
91
+ end
92
+
93
+ it 'should raise error if the right hand side is not an enumeration' do
94
+ lambda{ @bools.must_be.in true }.should raise_error(TypeError)
95
+ end
96
+
97
+ it 'should raise error if the right hand side does not contain tuples' do
98
+ lambda{ @bools.must_be.in [true, false] }.should raise_error(TypeError)
99
+ end
100
+
101
+ it 'should raise error if the right hand side does not contain boolean tuples' do
102
+ lambda{ @bools.must_be.in ['hello'] }.should raise_error(TypeError)
103
+ end
104
+
105
+ it_should_behave_like 'non-reifiable constraint'
106
+ end
data/specs/examples.rb ADDED
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'open3'
3
+
4
+ # This spec checks that the examples are still working.
5
+ files = Dir["#{File.dirname(__FILE__)}/../example/*.rb"]
6
+ files.delete_if do |file|
7
+ file =~ /example_helper.rb/
8
+ end
9
+
10
+ files.each do |example|
11
+ describe "Example (#{File.basename(example)})" do
12
+ it 'should not output errors' do
13
+ _, _, stderr = Open3.popen3("ruby #{example} 1> /dev/null")
14
+ stderr.gets.should be_nil
15
+ end
16
+ end
17
+ end
data/specs/search.rb CHANGED
@@ -205,6 +205,66 @@ describe Gecode::Model, '(optimization search)' do
205
205
  solution.z.value.should == 25
206
206
  end
207
207
 
208
+ it 'should support maximizing singe variables given as symbols' do
209
+ solution = SampleOptimizationProblem.new.maximize! :z
210
+ solution.should_not be_nil
211
+ solution.x.value.should == 5
212
+ solution.y.value.should == 5
213
+ solution.z.value.should == 25
214
+ end
215
+
216
+ it 'should support maximizing singe variables given as strings' do
217
+ solution = SampleOptimizationProblem.new.maximize! 'z'
218
+ solution.should_not be_nil
219
+ solution.x.value.should == 5
220
+ solution.y.value.should == 5
221
+ solution.z.value.should == 25
222
+ end
223
+
224
+ it 'should raise error if maximize! is given a non-existing method' do
225
+ lambda do
226
+ SampleOptimizationProblem.new.maximize! :does_not_exist
227
+ end.should raise_error(NameError)
228
+ end
229
+
230
+ it 'should raise error if maximize! is given a method that does not return an integer variable' do
231
+ lambda do
232
+ SampleOptimizationProblem.new.maximize! :object_id
233
+ end.should raise_error(ArgumentError)
234
+ end
235
+
236
+ it 'should support minimizing singe variables given as symbols' do
237
+ problem = SampleOptimizationProblem.new
238
+ problem.z.must > 2
239
+ solution = problem.minimize! :x
240
+ solution.should_not be_nil
241
+ solution.x.value.should == 1
242
+ solution.y.value.should == 3
243
+ solution.z.value.should == 3
244
+ end
245
+
246
+ it 'should support minimizing singe variables given as strings' do
247
+ problem = SampleOptimizationProblem.new
248
+ problem.z.must > 2
249
+ solution = problem.minimize! 'x'
250
+ solution.should_not be_nil
251
+ solution.x.value.should == 1
252
+ solution.y.value.should == 3
253
+ solution.z.value.should == 3
254
+ end
255
+
256
+ it 'should raise error if minimize! is given a non-existing method' do
257
+ lambda do
258
+ SampleOptimizationProblem.new.minimize! :does_not_exist
259
+ end.should raise_error(NameError)
260
+ end
261
+
262
+ it 'should raise error if minimize! is given a method that does not return an integer variable' do
263
+ lambda do
264
+ SampleOptimizationProblem.new.minimize! :object_id
265
+ end.should raise_error(ArgumentError)
266
+ end
267
+
208
268
  it 'should not be bothered by garbage collecting' do
209
269
  # This goes through 400+ spaces.
210
270
  solution = SampleOptimizationProblem2.new.optimize! do |model, best_so_far|
data/specs/set_var.rb CHANGED
@@ -47,7 +47,7 @@ end
47
47
  describe Gecode::FreeSetVar, '(assigned)' do
48
48
  before do
49
49
  model = Gecode::Model.new
50
- @var = model.set_var([1], [1])
50
+ @var = model.set_var(1, 1)
51
51
  model.solve!
52
52
  end
53
53
 
@@ -7,6 +7,7 @@ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
7
7
  PKG_FILE_NAME_WITH_GECODE = "#{PKG_NAME_WITH_GECODE}-#{PKG_VERSION}"
8
8
  # The location where the precompiled DLL should be placed.
9
9
  DLL_LOCATION = 'lib/gecode.dll'
10
+ EXT_DIR = 'ext'
10
11
 
11
12
  desc 'Generate RDoc'
12
13
  rd = Rake::RDocTask.new do |rdoc|
@@ -16,14 +17,19 @@ rd = Rake::RDocTask.new do |rdoc|
16
17
  rdoc.rdoc_files.include('README', 'CHANGES', 'LGPL-LICENSE', 'lib/**/*.rb')
17
18
  end
18
19
 
20
+ TMP_DIR = 'doc/tmp/rdoc_dev'
19
21
  desc 'Generate RDoc, ignoring nodoc'
20
- Rake::RDocTask.new(:rdoc_dev) do |rdoc|
22
+ Rake::RDocTask.new(:rdoc_dev => :prepare_rdoc_dev) do |rdoc|
21
23
  rdoc.rdoc_dir = 'doc/output/rdoc_dev'
22
24
  rdoc.options << '--title' << 'Gecode/R Developers RDoc' << '--line-numbers' <<
23
25
  '--inline-source' << '--accessor' << 'delegate'
24
-
26
+
27
+ rdoc.rdoc_files.include("#{TMP_DIR}/**/*.rb")
28
+ end
29
+
30
+ desc 'Copies the files that RDoc should parse, removing #:nodoc:'
31
+ task :prepare_rdoc_dev do
25
32
  # Copy the rdoc and remove all #:nodoc: .
26
- TMP_DIR = 'doc/tmp/rdoc_dev'
27
33
  Dir['lib/**/*.rb'].each do |source_name|
28
34
  destination_name = source_name.sub('lib', TMP_DIR)
29
35
  File.makedirs File.dirname(destination_name)
@@ -33,8 +39,6 @@ Rake::RDocTask.new(:rdoc_dev) do |rdoc|
33
39
  end
34
40
  destination.close
35
41
  end
36
-
37
- rdoc.rdoc_files.include("#{TMP_DIR}/**/*.rb")
38
42
  end
39
43
 
40
44
  spec = Gem::Specification.new do |s|
@@ -122,44 +126,55 @@ end
122
126
  desc 'Removes generated distribution files'
123
127
  task :clobber do
124
128
  rm DLL_LOCATION if File.exists? DLL_LOCATION
129
+ FileList[
130
+ "#{EXT_DIR}/*.o",
131
+ "#{EXT_DIR}/gecode.{cc,hh}",
132
+ "#{EXT_DIR}/Makefile",
133
+ "#{EXT_DIR}/mkmf.log"
134
+ ].to_a.each{ |file| rm file if File.exists? file }
125
135
  end
126
136
 
127
137
  desc 'Publish packages on RubyForge'
128
138
  task :publish_packages => [:publish_gecoder_packages,
129
139
  :publish_gecoder_with_gecode_packages]
130
140
 
141
+ # Files included in the vanilla Gecode/R release.
142
+ vanilla_release_files = [
143
+ "pkg/#{PKG_FILE_NAME}.gem",
144
+ "pkg/#{PKG_FILE_NAME}.tgz",
145
+ "pkg/#{PKG_FILE_NAME}.zip"
146
+ ]
147
+
131
148
  desc 'Publish Gecode/R packages on RubyForge'
132
- task :publish_gecoder_packages => [:verify_user, :package] do
133
- release_files = FileList[
134
- "pkg/#{PKG_FILE_NAME}.gem",
135
- "pkg/#{PKG_FILE_NAME}.tgz",
136
- "pkg/#{PKG_FILE_NAME}.zip"
137
- ]
149
+ task :publish_gecoder_packages => [:verify_user].concat(vanilla_release_files) do
138
150
  require 'meta_project'
139
151
  require 'rake/contrib/xforge'
140
152
 
141
153
  Rake::XForge::Release.new(MetaProject::Project::XForge::RubyForge.new(PROJECT_NAME)) do |xf|
142
154
  xf.user_name = ENV['RUBYFORGE_USER']
143
- xf.files = release_files.to_a
155
+ xf.files = vanilla_release_files.to_a
144
156
  xf.release_name = "Gecode/R #{PKG_VERSION}"
145
157
  xf.package_name = PKG_NAME
146
158
  end
147
159
  end
148
160
 
161
+ # Files included in the release with Gecode.
162
+ gecode_release_files = [
163
+ "pkg/#{PKG_FILE_NAME_WITH_GECODE}.gem",
164
+ "pkg/#{PKG_FILE_NAME_WITH_GECODE}.tgz",
165
+ "pkg/#{PKG_FILE_NAME_WITH_GECODE}.zip",
166
+ "pkg/#{PKG_FILE_NAME_WITH_GECODE}-mswin32.gem"
167
+ ]
168
+
149
169
  desc 'Publish Gecode/R with Gecode packages on RubyForge'
150
- task :publish_gecoder_with_gecode_packages => [:verify_user, :package] do
151
- release_files = FileList[
152
- "pkg/#{PKG_FILE_NAME_WITH_GECODE}*.gem",
153
- "pkg/#{PKG_FILE_NAME_WITH_GECODE}*.tgz",
154
- "pkg/#{PKG_FILE_NAME_WITH_GECODE}*.zip"
155
- ]
156
-
170
+ task :publish_gecoder_with_gecode_packages =>
171
+ [:verify_user].concat(gecode_release_files) do
157
172
  require 'meta_project'
158
173
  require 'rake/contrib/xforge'
159
174
 
160
175
  Rake::XForge::Release.new(MetaProject::Project::XForge::RubyForge.new(PROJECT_NAME)) do |xf|
161
176
  xf.user_name = ENV['RUBYFORGE_USER']
162
- xf.files = release_files.to_a
177
+ xf.files = gecode_release_files.to_a
163
178
  xf.release_name = "Gecode/R with Gecode #{PKG_VERSION}"
164
179
  xf.package_name = PKG_NAME_WITH_GECODE
165
180
  end
data/tasks/rcov.rake CHANGED
@@ -3,9 +3,10 @@ require 'spec/rake/verify_rcov'
3
3
 
4
4
  RCOV_DIR = "#{File.dirname(__FILE__)}/../doc/output/coverage"
5
5
 
6
- desc "Run all specs with rcov"
6
+ desc "Run all specs (except examples) with rcov"
7
7
  Spec::Rake::SpecTask.new(:rcov) do |t|
8
8
  t.spec_files = FileList['specs/**/*.rb']
9
+ t.spec_files.exclude 'examples.rb'
9
10
  t.rcov = true
10
11
  t.rcov_opts = ['--exclude examples', '--exclude specs']
11
12
  t.rcov_dir = RCOV_DIR
@@ -14,4 +15,4 @@ end
14
15
  RCov::VerifyTask.new(:verify_rcov => :rcov) do |t|
15
16
  t.threshold = 100.0
16
17
  t.index_html = "#{RCOV_DIR}/index.html"
17
- end
18
+ end
data/tasks/specs.rake CHANGED
@@ -3,13 +3,19 @@ require 'spec/rake/spectask'
3
3
  spec_files = FileList['specs/**/*.rb']
4
4
 
5
5
  desc 'Run all specs'
6
- Spec::Rake::SpecTask.new('specs') do |t|
6
+ Spec::Rake::SpecTask.new(:specs) do |t|
7
7
  t.libs = ['lib']
8
8
  t.spec_files = spec_files
9
9
  end
10
10
 
11
+ desc 'Run specs for the examples'
12
+ Spec::Rake::SpecTask.new(:example_specs) do |t|
13
+ t.libs = ['lib']
14
+ t.spec_files = FileList['specs/examples.rb']
15
+ end
16
+
11
17
  desc 'Generate an rspec html report'
12
- Spec::Rake::SpecTask.new('spec_html') do |t|
18
+ Spec::Rake::SpecTask.new(:spec_html) do |t|
13
19
  t.spec_files = spec_files
14
20
  t.spec_opts = ['--format html:doc/output/rspec.html','--backtrace']
15
- end
21
+ end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: gecoder
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.8.0
7
- date: 2008-04-10 00:00:00 +02:00
6
+ version: 0.8.1
7
+ date: 2008-04-20 00:00:00 +02:00
8
8
  summary: Ruby interface to Gecode, an environment for constraint programming.
9
9
  require_paths:
10
10
  - lib
@@ -34,7 +34,8 @@ files:
34
34
  - COPYING
35
35
  - README
36
36
  - LGPL-LICENSE
37
- - lib/gecoder/interface/constraints/bool_enum/boolean.rb
37
+ - lib/gecoder/interface/constraints/bool_enum/extensional.rb
38
+ - lib/gecoder/interface/constraints/bool_enum/relation.rb
38
39
  - lib/gecoder/interface/constraints/set_enum/operation.rb
39
40
  - lib/gecoder/interface/constraints/set_enum/distinct.rb
40
41
  - lib/gecoder/interface/constraints/set_enum/selection.rb
@@ -56,6 +57,7 @@ files:
56
57
  - lib/gecoder/interface/constraints/int_enum/count.rb
57
58
  - lib/gecoder/interface/constraints/int_enum/arithmetic.rb
58
59
  - lib/gecoder/interface/constraints/int_enum/element.rb
60
+ - lib/gecoder/interface/constraints/int_enum/extensional.rb
59
61
  - lib/gecoder/interface/constraints/int_enum_constraints.rb
60
62
  - lib/gecoder/interface/constraints/bool_enum_constraints.rb
61
63
  - lib/gecoder/interface/constraints/set_enum_constraints.rb
@@ -156,7 +158,6 @@ files:
156
158
  - tasks/dependencies.txt
157
159
  - specs/constraints
158
160
  - specs/constraints/boolean.rb
159
- - specs/constraints/bool_enum.rb
160
161
  - specs/constraints/int_domain.rb
161
162
  - specs/constraints/distinct.rb
162
163
  - specs/constraints/set_domain.rb
@@ -176,6 +177,8 @@ files:
176
177
  - specs/constraints/channel.rb
177
178
  - specs/constraints/linear.rb
178
179
  - specs/constraints/set_operation.rb
180
+ - specs/constraints/extensional.rb
181
+ - specs/constraints/bool_enum_relation.rb
179
182
  - specs/branch.rb
180
183
  - specs/model.rb
181
184
  - specs/binding_changes.rb
@@ -188,6 +191,7 @@ files:
188
191
  - specs/enum_matrix.rb
189
192
  - specs/spec_helper.rb
190
193
  - specs/distribution.rb
194
+ - specs/examples.rb
191
195
  - ext/missing.cpp
192
196
  - ext/vararray.cpp
193
197
  - ext/missing.h
@@ -195,7 +199,6 @@ files:
195
199
  - ext/extconf.rb
196
200
  test_files:
197
201
  - specs/constraints/boolean.rb
198
- - specs/constraints/bool_enum.rb
199
202
  - specs/constraints/int_domain.rb
200
203
  - specs/constraints/distinct.rb
201
204
  - specs/constraints/set_domain.rb
@@ -215,6 +218,8 @@ test_files:
215
218
  - specs/constraints/channel.rb
216
219
  - specs/constraints/linear.rb
217
220
  - specs/constraints/set_operation.rb
221
+ - specs/constraints/extensional.rb
222
+ - specs/constraints/bool_enum_relation.rb
218
223
  - specs/branch.rb
219
224
  - specs/model.rb
220
225
  - specs/binding_changes.rb
@@ -227,6 +232,7 @@ test_files:
227
232
  - specs/enum_matrix.rb
228
233
  - specs/spec_helper.rb
229
234
  - specs/distribution.rb
235
+ - specs/examples.rb
230
236
  rdoc_options:
231
237
  - --title
232
238
  - Gecode/R
@@ -240,7 +246,8 @@ extra_rdoc_files:
240
246
  - README
241
247
  - CHANGES
242
248
  - LGPL-LICENSE
243
- - lib/gecoder/interface/constraints/bool_enum/boolean.rb
249
+ - lib/gecoder/interface/constraints/bool_enum/extensional.rb
250
+ - lib/gecoder/interface/constraints/bool_enum/relation.rb
244
251
  - lib/gecoder/interface/constraints/set_enum/operation.rb
245
252
  - lib/gecoder/interface/constraints/set_enum/distinct.rb
246
253
  - lib/gecoder/interface/constraints/set_enum/selection.rb
@@ -262,6 +269,7 @@ extra_rdoc_files:
262
269
  - lib/gecoder/interface/constraints/int_enum/count.rb
263
270
  - lib/gecoder/interface/constraints/int_enum/arithmetic.rb
264
271
  - lib/gecoder/interface/constraints/int_enum/element.rb
272
+ - lib/gecoder/interface/constraints/int_enum/extensional.rb
265
273
  - lib/gecoder/interface/constraints/int_enum_constraints.rb
266
274
  - lib/gecoder/interface/constraints/bool_enum_constraints.rb
267
275
  - lib/gecoder/interface/constraints/set_enum_constraints.rb