feldtruby 0.4.7 → 0.4.8

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.
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