feldtruby 0.4.7 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -48,4 +48,5 @@ pkg
48
48
  # For rubinius:
49
49
  #*.rbc
50
50
 
51
- related/*
51
+ related/*
52
+ profiling/*
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- feldtruby (0.3.18)
4
+ feldtruby (0.4.7)
5
5
  bson_ext
6
6
  json
7
7
  mongo
@@ -11,12 +11,12 @@ PATH
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- bson (1.8.4)
15
- bson_ext (1.8.4)
16
- bson (~> 1.8.4)
14
+ bson (1.8.5)
15
+ bson_ext (1.8.5)
16
+ bson (~> 1.8.5)
17
17
  json (1.7.7)
18
- mongo (1.8.4)
19
- bson (~> 1.8.4)
18
+ mongo (1.8.5)
19
+ bson (~> 1.8.5)
20
20
  nokogiri (1.5.9)
21
21
  rinruby (2.0.3)
22
22
 
data/Rakefile CHANGED
@@ -55,7 +55,41 @@ task :clean do
55
55
  FileUtils.rm_rf "pkg"
56
56
  end
57
57
 
58
+ def profile_script(script, args = [])
59
+ ts = Time.now.strftime("%y%m%d_%H%M%S")
60
+ scriptname = File.basename(script)
61
+ profile = "./profiling/#{scriptname}_profile_#{ts}"
62
+
63
+ template = <<-EOS
64
+ require 'perftools'
65
+
66
+ PerfTools::CpuProfiler.start("PROFILENAME")
67
+ require "SCRIPT"
68
+ PerfTools::CpuProfiler.stop
69
+ EOS
70
+ profiling_script = template.gsub("PROFILENAME", profile).gsub("SCRIPT", script)
71
+ puts profiling_script
72
+ filename = "profiling/profiling_script.rb"
73
+ File.open(filename, "w") {|fh| fh.puts profiling_script}
74
+ psys "ruby #{filename} #{args.join(' ')}"
75
+ psys "pprof.rb --text #{profile} > #{profile}.txt"
76
+ psys "pprof.rb --pdf #{profile} > #{profile}.pdf"
77
+ File.delete(filename)
78
+ end
79
+
80
+ desc "Profile"
81
+ task :profile do
82
+ unless require("perftools")
83
+ puts "CANNOT profile since perftools.rb is NOT INSTALLED."
84
+ exit -1
85
+ end
86
+ profile_script "./spikes/simple_de_run.rb", ["100_000"]
87
+ profile_script "./spikes/long_running_search.rb"
88
+ end
89
+
58
90
  desc "Clean the repo of any files that should not be checked in"
59
- task :clobber => [:clean]
91
+ task :clobber => [:clean] do
92
+ system "rm -rf profiling/*"
93
+ end
60
94
 
61
95
  task :default => :test
@@ -50,6 +50,21 @@ class Array
50
50
  count_hash
51
51
  end
52
52
 
53
+ # Count the values that are within +/-(epsilon*targetValue) of a targetValue
54
+ # and return the counts as an array. Will skip elements of the array that are
55
+ # not Numeric.
56
+ def counts_within_ratio_of(targetValue, epsilon = 0.10)
57
+ min = targetValue * (1.0 - epsilon)
58
+ max = targetValue * (1.0 + epsilon)
59
+ count_hash = Hash.new(0)
60
+ self.each do |element|
61
+ if Numeric === element && element >= min && element <= max
62
+ count_hash[element] += 1
63
+ end
64
+ end
65
+ count_hash
66
+ end
67
+
53
68
  def sample
54
69
  self[rand(self.length)]
55
70
  end
@@ -41,6 +41,14 @@ class SearchSpace
41
41
  candidate.class.send(:[], *a)
42
42
  end
43
43
 
44
+ # Bound variable at _index_ in _candidate_.
45
+ def bound_at_index(index, candidate)
46
+ unless in_range_for_position?(candidate[index], index)
47
+ candidate[index] = gen_value_for_position(index)
48
+ end
49
+ candidate
50
+ end
51
+
44
52
  def in_range_for_position?(value, index)
45
53
  (value >= @min_values[index]) && (value <= @max_values[index])
46
54
  end
@@ -0,0 +1,86 @@
1
+ module FeldtRuby::Optimize
2
+
3
+ # A mutator mutates candidates.
4
+ class Mutator
5
+ end
6
+
7
+ class VectorMutator < Mutator
8
+ attr_reader :search_space
9
+
10
+ def initialize(optimizer, valueMutator = GaussianContinuousValueMutator.new)
11
+ @optimizer = optimizer
12
+ @search_space = optimizer.search_space
13
+ @value_mutator = valueMutator
14
+ @value_mutator.mutator = self
15
+ end
16
+
17
+ # Mutate a candidate and returned a mutated vector.
18
+ def mutate(candidate)
19
+ c = candidate.clone
20
+ index = rand(candidate.length)
21
+ c[index] = @value_mutator.mutate_value(value, index)
22
+ @search_space.bound_at_index(c, index)
23
+ end
24
+ end
25
+
26
+ # Random Gaussian variates from:
27
+ # http://stackoverflow.com/questions/5825680/code-to-generate-gaussian-normally-distributed-random-numbers-in-ruby
28
+ class RandomGaussian
29
+ def initialize(mean = 0.0, sd = 1.0)
30
+ @mean, @sd = mean, sd
31
+ @compute_next_pair = false
32
+ end
33
+
34
+ def sample
35
+ if (@compute_next_pair = !@compute_next_pair)
36
+ # Compute a pair of random values with normal distribution.
37
+ # See http://en.wikipedia.org/wiki/Box-Muller_transform
38
+ theta = 2 * Math::PI * rand()
39
+ scale = @sd * Math.sqrt(-2 * Math.log(1 - rand()))
40
+ @g1 = @mean + scale * Math.sin(theta)
41
+ @g0 = @mean + scale * Math.cos(theta)
42
+ else
43
+ @g1
44
+ end
45
+ end
46
+ end
47
+
48
+ # This will mutate with a gaussian sample of mean which is half the delta
49
+ # for the variable being mutated.
50
+ class GaussianContinuousValueMutator
51
+ def initialize
52
+ @gaussian = RandomGaussian.new(0.0, 1.0)
53
+ end
54
+
55
+ # Connect with the value mutator, calculate bounds etc.
56
+ def mutator=(mutator)
57
+ @mutator = mutator
58
+ @deltas = @mutator.search_space.deltas
59
+ end
60
+
61
+ def mutate_value_at_index(value, index)
62
+ sigma = @deltas[index]/2.0
63
+ value + (sigma * @gaussian.sample)
64
+ end
65
+ end
66
+
67
+ # A Stochastic Hill Climber will randomly mutate one variable of the candidate
68
+ # and then keep the new candidate if it was better.
69
+ class StochasticHillClimber < SingleCandidateOptimizer
70
+ DefaultOptions = {
71
+ :mutatorClass =>
72
+ }
73
+
74
+ def initialize_options(options)
75
+ super
76
+ @options = DefaultOptions.clone.update(options)
77
+ @mutator = @options[:mutatorClass].new(self)
78
+ @candidate = @search_space.gen_candidate
79
+ end
80
+
81
+ def optimization_step
82
+ [@mutator.mutate(@candidate), @candidate]
83
+ end
84
+ end
85
+
86
+ end
@@ -1,3 +1,3 @@
1
1
  module FeldtRuby
2
- VERSION = "0.4.7"
2
+ VERSION = "0.4.8"
3
3
  end
@@ -0,0 +1,21 @@
1
+ $: << "lib"
2
+ $: << "../lib"
3
+ $: << "."
4
+ require 'feldtruby/optimize/differential_evolution'
5
+ require 'test/long_running/single_objective_problems'
6
+
7
+ def best_from_de_on_objective(objective, dimensions, numSteps = 25_000,
8
+ verbose = true, optimizer = FeldtRuby::Optimize::DEOptimizer_Rand_1_Bin)
9
+ objective.dimensions = dimensions if objective.respond_to?(:dimensions=)
10
+ ss = objective.search_space
11
+ de = optimizer.new(objective, ss, {:verbose => verbose,
12
+ :maxNumSteps => numSteps})
13
+
14
+ start_time = Time.now
15
+ best = de.optimize().to_a
16
+ elapsed = Time.now - start_time
17
+
18
+ return best, objective, elapsed
19
+ end
20
+
21
+ best, obj, time = best_from_de_on_objective MinSphere.new, 30, 220_000
@@ -3,7 +3,6 @@ $: << "../lib"
3
3
  require 'feldtruby/optimize/differential_evolution'
4
4
 
5
5
  $NumSteps = (ARGV[0] && ARGV[0] =~ /^\d+/) ? ARGV[0].to_i : 10_000
6
- $LC = ((ARGV[1] || ARGV[0]) == "EventLogger") ? FeldtRuby::EventLogger : FeldtRuby::Logger
7
6
 
8
7
  class MinimizeRMS < FeldtRuby::Optimize::Objective
9
8
  def objective_min_rms(candidate)
@@ -15,10 +14,6 @@ class MinimizeRMSAndSum < MinimizeRMS
15
14
  def objective_min_sum(candidate)
16
15
  candidate.sum.abs
17
16
  end
18
-
19
- def new_default_logger
20
- $LC.new(STDOUT)
21
- end
22
17
  end
23
18
 
24
19
  include FeldtRuby::Optimize
@@ -36,7 +36,7 @@ def best_from_de_on_objective(objective, dimensions, numSteps = 25_000,
36
36
 
37
37
  val = objective.calc_func(best)
38
38
  val.must_be_close_to objective.minimum
39
- val.must_be :>, objective.minimum
39
+ val.must_be :>=, objective.minimum
40
40
 
41
41
  return best, objective, elapsed
42
42
  end
@@ -79,7 +79,7 @@ end
79
79
 
80
80
  describe "Easom function" do
81
81
  it 'can optimize the Easom function' do
82
- best, obj, time = best_from_de_on_objective MinBeale.new, nil, 10_000, false, FeldtRuby::Optimize::DEOptimizer_Best_1_Bin
82
+ best, obj, time = best_from_de_on_objective MinEasom.new, nil, 10_000, false, FeldtRuby::Optimize::DEOptimizer_Best_1_Bin
83
83
  best.must_be_close_to_one_solution_of obj
84
84
  time.must_be :<, 1.5
85
85
  end
data/test/test_array.rb CHANGED
@@ -107,6 +107,37 @@ describe "Array extensions" do
107
107
  end
108
108
  end
109
109
 
110
+ describe "counts_within_ratio_of" do
111
+ it "returns empty hash for empty array" do
112
+ [].counts_within_ratio_of(10).must_equal({})
113
+ end
114
+
115
+ it "counts right for arrays of only one number, when target is that number" do
116
+ [1].counts_within_ratio_of(1).must_equal({1 => 1})
117
+ [2, 2].counts_within_ratio_of(2).must_equal({2 => 2})
118
+ [4, 4, 4].counts_within_ratio_of(4).must_equal({4 => 3})
119
+ end
120
+
121
+ it "counts right for arrays of only one number, when target is far from that number" do
122
+ [1].counts_within_ratio_of(1000).must_equal({})
123
+ [2, 2].counts_within_ratio_of(1000).must_equal({})
124
+ [4, 4, 4].counts_within_ratio_of(1000).must_equal({})
125
+ end
126
+
127
+ it "counts right for arrays of many numbers, when target range includes them all" do
128
+ [97, 98, 97, 99, 100, 100, 101, 102, 103, 102].counts_within_ratio_of(100, 0.05).must_equal({
129
+ 97=>2, 98=>1, 99=>1, 100 => 2, 101=>1, 102 => 2, 103 => 1})
130
+ end
131
+
132
+ it "counts right for arrays of many numbers, when target range includes some of them" do
133
+ [97, 98, 97, 99, 100, 100, 101, 102, 103, 102, 1000, -20].counts_within_ratio_of(100, 0.01).must_equal({
134
+ 99=>1, 100 => 2, 101=>1})
135
+
136
+ [97, 98, 97, 99, 100, 100, 101, 102, 103, 102, 1000, -20].counts_within_ratio_of(100, 0.02).must_equal({
137
+ 98=>1, 99=>1, 100 => 2, 101=>1, 102 => 2})
138
+ end
139
+ end
140
+
110
141
  describe "sample" do
111
142
  it "only samples within the array" do
112
143
  d = (1..100).to_a
@@ -50,6 +50,41 @@ describe "SearchSpace#bound" do
50
50
  end
51
51
  end
52
52
 
53
+ describe "SearchSpace#bound_at_index" do
54
+ before do
55
+ @sp = FeldtRuby::Optimize::SearchSpace.new([-5, -3], [5, 7])
56
+ end
57
+
58
+ it "returns the values if they are INSIDE the search space boundaries" do
59
+ @sp.bound_at_index(0, [-1, 0]).must_equal [-1, 0]
60
+ @sp.bound_at_index(1, [-1, 0]).must_equal [-1, 0]
61
+ end
62
+
63
+ it "returns the values if they are ON the search space boundaries" do
64
+ @sp.bound_at_index(0, [-5, -3]).must_equal [-5, -3]
65
+ @sp.bound_at_index(1, [-5, -3]).must_equal [-5, -3]
66
+ @sp.bound_at_index(0, [ 5, 7]).must_equal [ 5, 7]
67
+ @sp.bound_at_index(1, [ 5, 7]).must_equal [ 5, 7]
68
+ end
69
+
70
+ it "generates a value INSIDE the search space boundaries when a value is given that is outside (negative, outside on one dimension)" do
71
+ l, h = @sp.bound_at_index(0, [-10, 3.4])
72
+ h.must_equal 3.4
73
+ l.must_be :>=, -5
74
+ l.must_be :<=, 5
75
+
76
+ l, h = @sp.bound_at_index(1, [-4.6, -4.1])
77
+ l.must_equal(-4.6)
78
+ h.must_be :>=, -3
79
+ h.must_be :<=, 7
80
+ end
81
+
82
+ it "does not touch a value outside the boundaries if it is not at the index" do
83
+ @sp.bound_at_index(1, [-10, 3.4]).must_equal [-10, 3.4]
84
+ @sp.bound_at_index(0, [-4.6, -4.1]).must_equal [-4.6, -4.1]
85
+ end
86
+ end
87
+
53
88
  describe "LatinHypercubeSampler" do
54
89
  before do
55
90
  @sampler = FeldtRuby::Optimize::SearchSpace::LatinHypercubeSampler.new
File without changes
metadata CHANGED
@@ -1,83 +1,94 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feldtruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.7
4
+ version: 0.4.8
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Robert Feldt
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-04-10 00:00:00.000000000 Z
12
+ date: 2013-04-11 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: rinruby
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
- - - '>='
19
+ - - ! '>='
18
20
  - !ruby/object:Gem::Version
19
21
  version: '0'
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
- - - '>='
27
+ - - ! '>='
25
28
  - !ruby/object:Gem::Version
26
29
  version: '0'
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: json
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
- - - '>='
35
+ - - ! '>='
32
36
  - !ruby/object:Gem::Version
33
37
  version: '0'
34
38
  type: :runtime
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
- - - '>='
43
+ - - ! '>='
39
44
  - !ruby/object:Gem::Version
40
45
  version: '0'
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: nokogiri
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
- - - '>='
51
+ - - ! '>='
46
52
  - !ruby/object:Gem::Version
47
53
  version: '0'
48
54
  type: :runtime
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
- - - '>='
59
+ - - ! '>='
53
60
  - !ruby/object:Gem::Version
54
61
  version: '0'
55
62
  - !ruby/object:Gem::Dependency
56
63
  name: mongo
57
64
  requirement: !ruby/object:Gem::Requirement
65
+ none: false
58
66
  requirements:
59
- - - '>='
67
+ - - ! '>='
60
68
  - !ruby/object:Gem::Version
61
69
  version: '0'
62
70
  type: :runtime
63
71
  prerelease: false
64
72
  version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
65
74
  requirements:
66
- - - '>='
75
+ - - ! '>='
67
76
  - !ruby/object:Gem::Version
68
77
  version: '0'
69
78
  - !ruby/object:Gem::Dependency
70
79
  name: bson_ext
71
80
  requirement: !ruby/object:Gem::Requirement
81
+ none: false
72
82
  requirements:
73
- - - '>='
83
+ - - ! '>='
74
84
  - !ruby/object:Gem::Version
75
85
  version: '0'
76
86
  type: :runtime
77
87
  prerelease: false
78
88
  version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
79
90
  requirements:
80
- - - '>='
91
+ - - ! '>='
81
92
  - !ruby/object:Gem::Version
82
93
  version: '0'
83
94
  description: Robert Feldt's Common Ruby Code lib
@@ -120,6 +131,7 @@ files:
120
131
  - lib/feldtruby/optimize/random_search.rb
121
132
  - lib/feldtruby/optimize/search_space.rb
122
133
  - lib/feldtruby/optimize/stdout_logger.rb
134
+ - lib/feldtruby/optimize/stochastic_hill_climber.rb
123
135
  - lib/feldtruby/optimize/sub_qualities_comparators.rb
124
136
  - lib/feldtruby/statistics.rb
125
137
  - lib/feldtruby/statistics/array_archive.rb
@@ -143,6 +155,7 @@ files:
143
155
  - spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_levi13_beale_easom_eggholder.csv
144
156
  - spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_levi13_beale_easom_eggholder_all_radii_4_to_30.csv
145
157
  - spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_omnitest.csv
158
+ - spikes/long_running_search.rb
146
159
  - spikes/mongodb_logger.rb
147
160
  - spikes/simple_de_run.rb
148
161
  - spikes/zlib_for_short_strings.rb
@@ -171,6 +184,7 @@ files:
171
184
  - test/test_optimize_populationbasedoptimizer.rb
172
185
  - test/test_optimize_random_search.rb
173
186
  - test/test_optimize_search_space.rb
187
+ - test/test_optimize_stochastic_hill_climber.rb
174
188
  - test/test_sax.rb
175
189
  - test/test_statistics.rb
176
190
  - test/test_string_distance.rb
@@ -182,26 +196,27 @@ files:
182
196
  - test/tmp_shorter.csv
183
197
  homepage: https://github.com/robertfeldt/feldtruby
184
198
  licenses: []
185
- metadata: {}
186
199
  post_install_message:
187
200
  rdoc_options: []
188
201
  require_paths:
189
202
  - lib
190
203
  required_ruby_version: !ruby/object:Gem::Requirement
204
+ none: false
191
205
  requirements:
192
- - - '>='
206
+ - - ! '>='
193
207
  - !ruby/object:Gem::Version
194
208
  version: '0'
195
209
  required_rubygems_version: !ruby/object:Gem::Requirement
210
+ none: false
196
211
  requirements:
197
- - - '>='
212
+ - - ! '>='
198
213
  - !ruby/object:Gem::Version
199
214
  version: '0'
200
215
  requirements: []
201
216
  rubyforge_project:
202
- rubygems_version: 2.0.0
217
+ rubygems_version: 1.8.25
203
218
  signing_key:
204
- specification_version: 4
219
+ specification_version: 3
205
220
  summary: Robert Feldt's Common Ruby Code lib
206
221
  test_files:
207
222
  - test/helper.rb
@@ -229,6 +244,7 @@ test_files:
229
244
  - test/test_optimize_populationbasedoptimizer.rb
230
245
  - test/test_optimize_random_search.rb
231
246
  - test/test_optimize_search_space.rb
247
+ - test/test_optimize_stochastic_hill_climber.rb
232
248
  - test/test_sax.rb
233
249
  - test/test_statistics.rb
234
250
  - test/test_string_distance.rb
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: c3705ba1e753ed80149d17c2f9da09263a8fa67a
4
- data.tar.gz: 44039e7195a0a8a5dd133637314570d80bf2aec0
5
- SHA512:
6
- metadata.gz: 2ade5807b5b3d85b3c64cca5b556f6bb510bc7062235e0a714846eb002eb8598581cbdcccaa423872267cd48285c934fa03f9e50ef869fa1da797b7b1af5d0dc
7
- data.tar.gz: 8f159aeb892511a04db531b587ecb8d5ffab0d83e38b5aa796e70cc151fe956764c5501099f16c22fb875d680678a7662d60c69209655e1fee9f6ab774ca763a