gecoder 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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'