gecoder 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -125,7 +125,7 @@ namespace Gecode {
125
125
  * MDFS is the same as DFS but with the size of a space inferred from the
126
126
  * other arguments, since it can't be done from the Ruby side.
127
127
  */
128
- MDFS::MDFS(MSpace* space, unsigned int c_d, unsigned int a_d, Search::Stop* st) : Gecode::Search::DFS(space, c_d, a_d, st, sizeof(space)) {
128
+ MDFS::MDFS(MSpace* space, const Search::Options &o) : Gecode::DFS<MSpace>(space, o) {
129
129
  }
130
130
 
131
131
  MDFS::~MDFS() {
@@ -143,39 +143,35 @@ namespace Gecode {
143
143
 
144
144
  namespace Search {
145
145
  /*
146
- * MStop combines the time stop and fail stop, but for what reason?
147
- * TODO: Try removing MStop, replacing it with plain Stop objects.
146
+ * MStop combines the time, memory and fail stop into one concrete
147
+ * class. This is done because the bindings generator can't deal
148
+ * with virtual classes.
148
149
  */
149
150
 
150
151
  struct MStop::Private {
151
152
  Gecode::Search::TimeStop* ts;
152
153
  Gecode::Search::FailStop* fs;
154
+ Gecode::Search::MemoryStop* ms;
153
155
  };
154
156
 
155
157
  MStop::MStop() : d(new Private) {
156
158
  d->ts = 0;
157
159
  d->fs = 0;
160
+ d->ms = 0;
158
161
  }
159
162
 
160
- MStop::MStop(int fails, int time) : d(new Private) {
163
+ MStop::MStop(int fails, int time, size_t mem) : d(new Private) {
161
164
  d->ts = new Search::TimeStop(time);
162
165
  d->fs = new Search::FailStop(fails);
166
+ d->ms = new Search::MemoryStop(mem);
163
167
  }
164
168
 
165
169
  MStop::~MStop() {
166
170
  }
167
171
 
168
172
  bool MStop::stop(const Gecode::Search::Statistics &s) {
169
- if (!d->fs && !d->ts) return false;
170
- return d->fs->stop(s) || d->ts->stop(s);
171
- }
172
-
173
- Gecode::Search::Stop* MStop::create(int fails, int time) {
174
- if (fails < 0 && time < 0) return 0;
175
- if (fails < 0) return new Search::TimeStop(time);
176
- if (time < 0) return new Search::FailStop(fails);
177
-
178
- return new MStop(fails, time);
173
+ if (!d->fs && !d->ts && !d->ms) return false;
174
+ return d->fs->stop(s) || d->ts->stop(s) || d->ms->stop(s);
179
175
  }
180
176
  }
181
177
  }
@@ -58,9 +58,9 @@ namespace Gecode {
58
58
  Gecode::SetVarArray set_variables;
59
59
  };
60
60
 
61
- class MDFS : public Gecode::Search::DFS {
61
+ class MDFS : public Gecode::DFS<MSpace> {
62
62
  public:
63
- MDFS(MSpace *space, unsigned int c_d, unsigned int a_d, Search::Stop* st = 0);
63
+ MDFS(MSpace *space, const Search::Options &o);
64
64
  ~MDFS();
65
65
  };
66
66
 
@@ -72,15 +72,12 @@ namespace Gecode {
72
72
 
73
73
  namespace Search {
74
74
  class MStop : public Gecode::Search::Stop {
75
- private:
76
- MStop(int fails, int time);
77
-
78
75
  public:
79
76
  MStop();
77
+ MStop(int fails, int time, size_t mem);
80
78
  ~MStop();
81
79
 
82
80
  bool stop (const Gecode::Search::Statistics &s);
83
- static Gecode::Search::Stop* create(int fails, int time);
84
81
 
85
82
  private:
86
83
  struct Private;
@@ -38,10 +38,10 @@ Gecode::IntArgs ruby2Gecode_IntArgs(VALUE arr, int argn = -1);
38
38
  Gecode::IntArgs ruby2Gecode_IntArgs(VALUE arr, int argn)
39
39
  {
40
40
  RArray *array = RARRAY(arr);
41
- Gecode::IntArgs intargs(array->len);
42
- for(int i = 0; i < array->len; i++)
41
+ Gecode::IntArgs intargs(RARRAY_LEN(array));
42
+ for(int i = 0; i < RARRAY_LEN(array); i++)
43
43
  {
44
- intargs[i] = NUM2INT(array->ptr[i]);
44
+ intargs[i] = NUM2INT(RARRAY_PTR(array)[i]);
45
45
  }
46
46
 
47
47
  return intargs;
@@ -628,12 +628,11 @@ Rust::Bindings::create_bindings Rust::Bindings::LangCxx, "gecode" do |b|
628
628
  klass.bindname = "DFS"
629
629
  klass.add_constructor do |method|
630
630
  method.add_parameter "Gecode::MSpace *", "s"
631
- method.add_parameter "int", "c_d"
632
- method.add_parameter "int", "a_d"
633
- method.add_parameter "Gecode::Search::MStop *", "st"
631
+ method.add_parameter "Gecode::Search::Options", "o"
634
632
  end
635
633
 
636
634
  klass.add_method "next", "Gecode::MSpace *"
635
+ klass.add_method "stopped", "bool"
637
636
  klass.add_method "statistics", "Gecode::Search::Statistics"
638
637
  end
639
638
 
@@ -645,6 +644,7 @@ Rust::Bindings::create_bindings Rust::Bindings::LangCxx, "gecode" do |b|
645
644
  end
646
645
 
647
646
  klass.add_method "next", "Gecode::MSpace *"
647
+ klass.add_method "stopped", "bool"
648
648
  klass.add_method "statistics", "Gecode::Search::Statistics"
649
649
  end
650
650
 
@@ -705,6 +705,11 @@ Rust::Bindings::create_bindings Rust::Bindings::LangCxx, "gecode" do |b|
705
705
  searchns.add_cxx_class "MStop" do |klass|
706
706
  klass.bindname = "Stop"
707
707
  klass.add_constructor
708
+ klass.add_constructor do |method|
709
+ method.add_parameter "int", "fails"
710
+ method.add_parameter "int", "time"
711
+ method.add_parameter "int", "mem"
712
+ end
708
713
  end
709
714
 
710
715
  searchns.add_cxx_class "Statistics" do |klass|
@@ -120,6 +120,10 @@ module Gecode
120
120
  # See e.g. Gecode::Int::IntConstraintReceiver for
121
121
  # concrete examples of options being specified.
122
122
  class ConstraintReceiver
123
+ # A list that keeps track of all constraint methods used in the
124
+ # program, essentially providing unique ids for each.
125
+ @@constraint_names_list ||= []
126
+
123
127
  # Constructs a new expression with the specified parameters. The
124
128
  # parameters should at least contain the keys :lhs, and :negate.
125
129
  #
@@ -150,8 +154,12 @@ module Gecode
150
154
  # The original constraint method is assumed to take two arguments:
151
155
  # a right hand side and a hash of options.
152
156
  def self.provide_commutativity(constraint_name, &block)
153
- unique_id = constraint_name.to_sym.to_i
154
- pre_alias_method_name = 'pre_commutivity_' << unique_id.to_s
157
+ unless @@constraint_names_list.include? constraint_name
158
+ @@constraint_names_list << constraint_name
159
+ end
160
+ unique_id = @@constraint_names_list.index(constraint_name)
161
+
162
+ pre_alias_method_name = "pre_commutivity_#{unique_id}"
155
163
  if method_defined? constraint_name
156
164
  alias_method pre_alias_method_name, constraint_name
157
165
  end
@@ -86,10 +86,14 @@ module Gecode
86
86
  # FalseClass or Enumerable.
87
87
  def self.internal_parse_regexp(arg)
88
88
  case arg
89
- when Gecode::Raw::REG: arg
90
- when Fixnum: Gecode::Raw::REG.new(arg)
91
- when TrueClass: Gecode::Raw::REG.new(1)
92
- when FalseClass: Gecode::Raw::REG.new(0)
89
+ when Gecode::Raw::REG
90
+ arg
91
+ when Fixnum
92
+ Gecode::Raw::REG.new(arg)
93
+ when TrueClass
94
+ Gecode::Raw::REG.new(1)
95
+ when FalseClass
96
+ Gecode::Raw::REG.new(0)
93
97
  when Enumerable
94
98
  # Recursively convert the elements of the arg.
95
99
  arg.inject(Gecode::Raw::REG.new) do |regexp, element|
@@ -48,8 +48,9 @@ module Gecode::Set
48
48
  end
49
49
 
50
50
  case options.keys.first
51
- when :substitutions: subs = options[:substitutions]
52
- when :weights:
51
+ when :substitutions
52
+ subs = options[:substitutions]
53
+ when :weights
53
54
  weights = options[:weights]
54
55
  subs = Hash.new do |hash, key|
55
56
  if weights[key].nil?
@@ -1,7 +1,7 @@
1
1
  module Gecode::SetEnum
2
2
  class SetEnumConstraintReceiver
3
3
  # Constrains this set enum to channel +int_enum_operand+. The i:th set
4
- # in the enumeration of set operands is constrained to includes the value
4
+ # in the enumeration of set operands is constrained to include the value
5
5
  # of the j:th integer operand.
6
6
  #
7
7
  # Neither reification nor negation is supported.
@@ -6,15 +6,32 @@ module Gecode
6
6
  super('No solution could be found.')
7
7
  end
8
8
  end
9
+
10
+ # An exception raised when a search has been aborted due to e.g.
11
+ # hitting the time limit specified when initiating the search.
12
+ class SearchAbortedError < RuntimeError
13
+ def initialize #:nodoc:
14
+ super('The search was aborted before a solution could be found.')
15
+ end
16
+ end
9
17
 
10
18
  module Mixin
11
19
  # Finds the first solution to the modelled problem and updates the variables
12
20
  # to that solution. The found solution is also returned. Raises
13
21
  # Gecode::NoSolutionError if no solution can be found.
14
- def solve!
15
- dfs = dfs_engine
22
+ #
23
+ # The following options can be specified in a hash with symbols as
24
+ # keys when calling the method:
25
+ #
26
+ # [:time_limit] The number of milliseconds that the solver should be
27
+ # allowed to use when searching for a solution. If it can
28
+ # not find a solution fast enough, then
29
+ # Gecode::SearchAbortedError is raised.
30
+ def solve!(options = {})
31
+ dfs = dfs_engine(options)
16
32
  space = dfs.next
17
33
  @gecoder_mixin_statistics = dfs.statistics
34
+ raise Gecode::SearchAbortedError if dfs.stopped
18
35
  raise Gecode::NoSolutionError if space.nil?
19
36
  self.active_space = space
20
37
  return self
@@ -184,15 +201,29 @@ module Gecode
184
201
 
185
202
  # Creates a depth first search engine for search, executing any
186
203
  # unexecuted constraints first.
187
- def dfs_engine
204
+ def dfs_engine(options = {})
188
205
  # Execute constraints.
189
206
  perform_queued_gecode_interactions
190
207
 
208
+ # Begin constructing the option struct.
209
+ opt_struct = Gecode::Raw::Search::Options.new
210
+ opt_struct.c_d = Gecode::Raw::Search::Config::MINIMAL_DISTANCE
211
+ opt_struct.a_d = Gecode::Raw::Search::Config::ADAPTIVE_DISTANCE
212
+
213
+ # Decode the options.
214
+ if options.has_key? :time_limit
215
+ opt_struct.stop = Gecode::Raw::Search::Stop.new(-1, options.delete(:time_limit), -1)
216
+ else
217
+ opt_struct.stop = nil
218
+ end
219
+
220
+ unless options.empty?
221
+ raise ArgumentError, 'Unrecognized search option: ' +
222
+ options.keys.first.to_s
223
+ end
224
+
191
225
  # Construct the engine.
192
- Gecode::Raw::DFS.new(selected_space,
193
- Gecode::Raw::Search::Config::MINIMAL_DISTANCE,
194
- Gecode::Raw::Search::Config::ADAPTIVE_DISTANCE,
195
- nil)
226
+ Gecode::Raw::DFS.new(selected_space, opt_struct)
196
227
  end
197
228
  end
198
229
  end
@@ -1,4 +1,4 @@
1
1
  module GecodeR #:nodoc:
2
2
  # A string representation of the Gecode/R version.
3
- VERSION = '1.0.0'
3
+ VERSION = '1.1.0'
4
4
  end
@@ -21,6 +21,23 @@ class SampleProblem
21
21
  end
22
22
  end
23
23
 
24
+ class DifficultSampleProblem
25
+ include Gecode::Mixin
26
+
27
+ def initialize
28
+ # The magic sequence problem with a large n.
29
+ n = 500
30
+ sequence_is_an int_var_array(n, 0...n)
31
+ n.times{ |i| sequence.count(i).must == sequence[i] }
32
+ sequence.inject{ |x,y| x + y }.must == n
33
+ sequence.zip((-1...n).to_a).map do |element, c|
34
+ element*c
35
+ end.inject{ |x,y| x + y }.must == 0
36
+
37
+ branch_on sequence, :variable => :smallest_degree, :value => :split_max
38
+ end
39
+ end
40
+
24
41
  class SampleOptimizationProblem
25
42
  include Gecode::Mixin
26
43
 
@@ -387,3 +404,29 @@ describe Gecode::Mixin, '(single variable maximization)' do
387
404
  it_should_behave_like 'single variable optimization'
388
405
  end
389
406
 
407
+ describe Gecode::Mixin, ' (with time limitations)' do
408
+ it 'should not time out problems that finish within the time limitation' do
409
+ @domain = 0..3
410
+ @solved_domain = [2]
411
+ @model = SampleProblem.new(@domain)
412
+ lambda do
413
+ @model.solve!(:time_limit => 50)
414
+ end.should_not raise_error(Gecode::SearchAbortedError)
415
+ end
416
+
417
+ it 'should time out problems that do not finish within the time limitation' do
418
+ @model = DifficultSampleProblem.new
419
+ lambda do
420
+ t = Time.now
421
+ @model.solve!(:time_limit => 50)
422
+ puts Time.now - t
423
+ end.should raise_error(Gecode::SearchAbortedError)
424
+ end
425
+
426
+ it 'should raise error if an unrecognised option is passed' do
427
+ @model = DifficultSampleProblem.new
428
+ lambda do
429
+ @model.solve!(:time_limit => 50, :foo => 1)
430
+ end.should raise_error(ArgumentError)
431
+ end
432
+ end
@@ -18,18 +18,20 @@ rd = Rake::RDocTask.new do |rdoc|
18
18
  rdoc.rdoc_dir = 'doc/output/rdoc'
19
19
  rdoc.title = 'Gecode/R'
20
20
  rdoc.template = 'doc/rdoc/jamis.rb'
21
- rdoc.options << '--line-numbers' << '--inline-source' <<
21
+ rdoc.options << '--line-numbers' << '--inline-source' <<
22
22
  '--accessor' << 'delegate' << '--main' << 'README'
23
23
  rdoc.rdoc_files.include('README', 'CHANGES', 'THANKS', 'LGPL-LICENSE', 'lib/**/*.rb')
24
24
  end
25
25
 
26
+ # Removing this, which apparently doesn't work due to an update to RDoc.
27
+ =begin
26
28
  TMP_DIR = 'doc/tmp/rdoc_dev'
27
29
  desc 'Generate RDoc, ignoring nodoc'
28
30
  Rake::RDocTask.new(:rdoc_dev => :prepare_rdoc_dev) do |rdoc|
29
31
  rdoc.rdoc_dir = 'doc/output/rdoc_dev'
30
- rdoc.options << '--title' << 'Gecode/R Developers RDoc' << '--line-numbers' <<
32
+ rdoc.options << '--title' << 'Gecode/R Developers RDoc' << '--line-numbers' <<
31
33
  '--inline-source' << '--accessor' << 'delegate'
32
-
34
+
33
35
  rdoc.rdoc_files.include("#{TMP_DIR}/**/*.rb")
34
36
  end
35
37
 
@@ -46,6 +48,7 @@ task :prepare_rdoc_dev do
46
48
  destination.close
47
49
  end
48
50
  end
51
+ =end
49
52
 
50
53
  desc 'Extracts the source of Gecode before it is packaged into a gem'
51
54
  task :extract_gecode do
@@ -53,25 +56,25 @@ task :extract_gecode do
53
56
  cd(EXT_DIR) do
54
57
  cp "../#{GECODE_DIR}/#{GECODE_ARCHIVE_NAME}", GECODE_ARCHIVE_NAME
55
58
  system("tar -xvf #{GECODE_ARCHIVE_NAME}")
56
- rm GECODE_ARCHIVE_NAME
59
+ rm GECODE_ARCHIVE_NAME
57
60
  end
58
61
  end
59
62
  # To ensure that the Gecode files exist for the gem spec.
60
- Rake::Task['extract_gecode'].invoke
63
+ Rake::Task['extract_gecode'].invoke
61
64
 
62
65
  spec = Gem::Specification.new do |s|
63
66
  s.name = PKG_NAME
64
67
  s.version = GecodeR::VERSION
65
68
  s.summary = 'Ruby interface to Gecode, an environment for constraint programming.'
66
69
  s.description = <<-end_description
67
- Gecode/R is a Ruby interface to the Gecode constraint programming library.
68
- Gecode/R is intended for people with no previous experience of constraint
70
+ Gecode/R is a Ruby interface to the Gecode constraint programming library.
71
+ Gecode/R is intended for people with no previous experience of constraint
69
72
  programming, aiming to be easy to pick up and use.
70
73
  end_description
71
74
 
72
75
  s.files = FileList[
73
76
  '[A-Z]*',
74
- 'lib/**/*.rb',
77
+ 'lib/**/*.rb',
75
78
  'example/**/*.rb',
76
79
  'src/**/*',
77
80
  'vendor/rust/**/*',
@@ -98,12 +101,12 @@ spec = Gem::Specification.new do |s|
98
101
  =begin
99
102
  # Development dependencies.
100
103
  # Not listed: rubygems >= 1.2
101
- [['rake'],
102
- ['webgen', '= 0.4.7'],
103
- ['coderay'],
104
- ['rspec', '>= 1.0'],
105
- ['rcov'],
106
- ['meta_project'],
104
+ [['rake'],
105
+ ['webgen', '= 0.4.7'],
106
+ ['coderay'],
107
+ ['rspec', '>= 1.0'],
108
+ ['rcov'],
109
+ ['meta_project'],
107
110
  ['rubyforge']].each do |dependency|
108
111
  s.add_development_dependency(*dependency)
109
112
  end
@@ -117,17 +120,17 @@ spec_windows_binary_with_gecode.extensions = []
117
120
  spec_windows_binary_with_gecode.requirements = []
118
121
  # Add the precompiled Gecode DLLs and precompiled bindings.
119
122
  spec_windows_binary_with_gecode.files = spec.files.dup -
120
- FileList['ext/**/*'].to_a +
123
+ FileList['ext/**/*'].to_a +
121
124
  FileList['vendor/gecode/win32/lib/*'].to_a << 'lib/gecode.dll'
122
125
  spec_windows_binary_with_gecode.platform = 'x86-mswin32-60' #Gem::Platform::WIN32
123
126
 
124
127
  # Create a clone of the gem spec that includes Gecode.
125
128
  spec_with_gecode = spec.dup
126
129
  spec_with_gecode.name = PKG_NAME_WITH_GECODE
127
- spec_with_gecode.extensions =
130
+ spec_with_gecode.extensions =
128
131
  spec_with_gecode.extensions.dup.unshift 'ext/gecode-2.2.0/configure'
129
132
  spec_with_gecode.requirements = []
130
- spec_with_gecode.files = spec.files.dup + FileList['ext/gecode-*/**/*'].to_a
133
+ spec_with_gecode.files = spec.files.dup + FileList['ext/gecode-*/**/*'].to_a
131
134
 
132
135
  desc 'Generate Gecode/R Gem'
133
136
  Rake::GemPackageTask.new(spec) do |pkg|
@@ -141,9 +144,11 @@ Rake::GemPackageTask.new(spec_with_gecode) do |pkg|
141
144
  pkg.need_tar = true
142
145
  end
143
146
 
147
+ =begin
144
148
  desc 'Generate Gecode/R + Gecode Gem (windows binary)'
145
149
  Rake::GemPackageTask.new(spec_windows_binary_with_gecode) do |pkg|
146
150
  end
151
+ =end
147
152
 
148
153
  desc 'Precompiles the Gecode/R bindings for Windows platforms'
149
154
  file 'lib/gecode.dll' do
@@ -168,7 +173,7 @@ task :clobber do
168
173
  end
169
174
 
170
175
  desc 'Publish packages on RubyForge'
171
- task :publish_packages => [:publish_gecoder_packages,
176
+ task :publish_packages => [:publish_gecoder_packages,
172
177
  :publish_gecoder_with_gecode_packages]
173
178
 
174
179
  # Files included in the vanilla Gecode/R release.
@@ -195,13 +200,13 @@ gecode_release_files = [
195
200
  "pkg/#{PKG_FILE_NAME_WITH_GECODE}.gem",
196
201
  #"pkg/#{PKG_FILE_NAME_WITH_GECODE}.tgz",
197
202
  #"pkg/#{PKG_FILE_NAME_WITH_GECODE}.zip",
198
- "pkg/#{PKG_FILE_NAME_WITH_GECODE}-x86-mswin32-60.gem"
203
+ #"pkg/#{PKG_FILE_NAME_WITH_GECODE}-x86-mswin32-60.gem"
199
204
  ]
200
205
  gecode_release_files.each do |pkg|
201
206
  file pkg => :extract_gecode
202
207
  end
203
208
  desc 'Publish Gecode/R with Gecode packages on RubyForge'
204
- task :publish_gecoder_with_gecode_packages =>
209
+ task :publish_gecoder_with_gecode_packages =>
205
210
  [:verify_user] + gecode_release_files do
206
211
  require 'meta_project'
207
212
  require 'rake/contrib/xforge'