feldtruby 0.4.4 → 0.4.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f017a746cfb0c942893d7207ddcd0ec3d3a2a252
4
- data.tar.gz: 475222949c01be1ae9dbb5678b4f2da00bf5a877
3
+ metadata.gz: b5558f3b77d1f52c3bb73f77ab79755b9d8e8f20
4
+ data.tar.gz: 1d6e7c4cea54211d5487b41b71710ea30827ceb8
5
5
  SHA512:
6
- metadata.gz: b85a1f419930ec29d072227bfa3423b3088f2befc4755d60df2cf8f870407d35cb84b2684074917c38aa3904e697476bc6ea9d5ecb633e0692e9a2c274fb6c02
7
- data.tar.gz: f825a6ee4384822a133f39df4f8c1867f42e611e2cdb307bcf2eaf3771860435f2ed1cd7440210b0841c2040f83e4affd1744023350154e40095dd272679b6d1
6
+ metadata.gz: d7f2ff3e1edcacd55278c11d228eced97c128ce1628f55f5edb30aee3fdfb6c2a7c676ac62860a62e5cfd33bd588eba795d50f489becbaae592bb63b36f8b07b
7
+ data.tar.gz: 2397c74ee5a6eea1c250b9481ace101ed6ecc58990649b2fa6d10aaee73136a5b5e758ccd7689c807f1283f49493c7e3ac1987dea11b37fe4fe262f6e8474020
@@ -9,7 +9,7 @@ module FeldtRuby
9
9
  class Logger
10
10
  DefaultParams = {
11
11
  :verbose => false,
12
- :printFrequency => 0.4 # Minimum seconds between consecutive messages printed for the same event type
12
+ :printFrequency => 0.3 # Minimum seconds between consecutive messages printed for the same event type
13
13
  }
14
14
 
15
15
  UnixEpoch = Time.at(0)
@@ -67,7 +67,7 @@ class DEOptimizerBase < EvolutionaryOptimizer
67
67
  "Trial Quality" => @objective.quality_of(trial),
68
68
  "Target" => target,
69
69
  "Target Quality" => @objective.quality_of(target)
70
- }, "DE (step #{@num_optimization_steps}): Trial vector was better than target vector", true
70
+ }, "DE (step #{@num_optimization_steps}): Trial vector was better than target vector"
71
71
  update_candidate_in_population(target_index, trial)
72
72
  feedback_on_trial_vs_target(trial, target, true)
73
73
  else
@@ -142,49 +142,58 @@ module DE_CrossoverStrategy_Binomial
142
142
  end
143
143
  end
144
144
 
145
- # Building block for mutation strategies.
146
- module DE_X_1_StrategyBuildingBlock
147
- # We need 0 target parents and 2 other parents. Note that we must sample a target parent
148
- # also even though it is not used in the mutation.
149
- def num_parents_to_sample; 3; end
150
-
151
- def difference_vector(donorParentsIndices)
152
- p1, p2 = get_candidates_with_indices(donorParentsIndices)
153
- (p1 - p2)
154
- end
155
- end
145
+ # The most-used DE/rand/1/* mutation strategy.
146
+ module DE_MutationStrategy_Rand_1
147
+ # We need three parents for donor vector. And then the target, so 1+3 in total.
148
+ def num_parents_to_sample; 4; end
156
149
 
157
- module DE_Rand_X_StrategyBuildingBlock
158
150
  def mutate(targetIndex, donorParentsIndices)
159
- p3 = get_candidate(donorParentsIndices[-1])
160
- p3 + scale_factor(targetIndex) * difference_vector(donorParentsIndices[0...-1])
151
+ p1, p2, p3 = get_candidates_with_indices(donorParentsIndices)
152
+ p3 + (scale_factor(targetIndex) * (p1 - p2))
161
153
  end
162
154
  end
163
155
 
164
- # The most-used DE/rand/1 mutation strategy.
165
- module DE_MutationStrategy_Rand_1
166
- include DE_X_1_StrategyBuildingBlock
156
+ # DE/rand/1/bin uses
157
+ # Bounding = random bounding within the search space
158
+ # Update = no updates based on feedback
159
+ # Crossover = Classic binomial
160
+ # Mutation = Rand-1
161
+ class DEOptimizer_Rand_1_Bin < DEOptimizerBase
162
+ include DE_BoundingStrategy_RandomWithinSearchSpace
163
+ include DE_UpdateStrategy_NoFeedbackUpdates
164
+ include DE_CrossoverStrategy_Binomial
165
+ include DE_MutationStrategy_Rand_1
166
+ end
167
167
 
168
- # We need one more parent in the Rand strategy than in the others, but
169
- # we can reuse the difference vector generation. So partial reuse here
170
- # NOTE! Order of inclusion is critical!!!
171
- def num_parents_to_sample; 4; end
168
+ # The DE/best/1/* mutation strategy.
169
+ module DE_MutationStrategy_Best_1
170
+ # We need two parents for donor vector. And then the target, so 1+2 in total.
171
+ def num_parents_to_sample; 3; end
172
172
 
173
- include DE_Rand_X_StrategyBuildingBlock
173
+ def mutate(targetIndex, donorParentsIndices)
174
+ p1, p2 = get_candidates_with_indices(donorParentsIndices)
175
+ candidate_from_array(best) + (scale_factor(targetIndex) * (p1 - p2))
176
+ end
174
177
  end
175
178
 
176
- # The default DEOptimizer uses
177
- # Bounding = random bouding within the search space
179
+ # DE/best/1/bin uses
180
+ # Bounding = random bounding within the search space
178
181
  # Update = no updates based on feedback
179
182
  # Crossover = Classic binomial
180
- # Mutation = Rand-1
181
- class DEOptimizer < DEOptimizerBase
183
+ # Mutation = Best-1
184
+ class DEOptimizer_Best_1_Bin < DEOptimizerBase
182
185
  include DE_BoundingStrategy_RandomWithinSearchSpace
183
186
  include DE_UpdateStrategy_NoFeedbackUpdates
184
187
  include DE_CrossoverStrategy_Binomial
185
- include DE_MutationStrategy_Rand_1
188
+ include DE_MutationStrategy_Best_1
186
189
  end
187
190
 
191
+ # DE/rand/1/bin is the default DE optimizer since it does not converge too
192
+ # quickly but is generally good. For many problems the DEOptimizer_Best_1_Bin
193
+ # gives better results faster though.
194
+ DEOptimizer = DEOptimizer_Rand_1_Bin
195
+ #DEOptimizer = DEOptimizer_Best_1_Bin
196
+
188
197
  # Optimize the _numVariables_ between the _min_ and _max_ values given _costFunction_.
189
198
  # Default is to minimize.
190
199
  def self.optimize(min, max, options = {:verbose => true},
@@ -294,9 +294,6 @@ class Objective
294
294
  :type_of_improvement => typeOfReset
295
295
  }, "Better candidate found for goal #{goal_methods[index]}"
296
296
 
297
- # Reset the best object since we have a new scale
298
- @best_candidate = nil
299
-
300
297
  end
301
298
 
302
299
  inc_version_number
@@ -1,3 +1,3 @@
1
1
  module FeldtRuby
2
- VERSION = "0.4.4"
2
+ VERSION = "0.4.5"
3
3
  end
@@ -169,7 +169,7 @@ end
169
169
  class MinEggHolder < Min2DSingleObjectiveFunc
170
170
  def minimum
171
171
  # -959.6407
172
- -963.5808501270542
172
+ -963.5808501270571
173
173
  end
174
174
 
175
175
  def min_solutions
@@ -19,10 +19,15 @@ module MiniTest::Expectations
19
19
  infect_an_assertion :assert_close_to_one_solution, :must_be_close_to_one_solution_of
20
20
  end
21
21
 
22
- def best_from_de_on_objective(objective, dimensions, numSteps = 25_000, verbose = false)
22
+ # Note! We have set the numSteps below so that DEOptimizer_Rand_1_Bin can converge
23
+ # to a solution. However, for most of these optimization problems
24
+ # DEOptimizer_Best_1_Bin converges much faster, and can thus be used.
25
+
26
+ def best_from_de_on_objective(objective, dimensions, numSteps = 25_000,
27
+ verbose = false, optimizer = FeldtRuby::Optimize::DEOptimizer_Rand_1_Bin)
23
28
  objective.dimensions = dimensions if objective.respond_to?(:dimensions=)
24
29
  ss = objective.search_space
25
- de = DEOptimizer.new(objective, ss, {:verbose => verbose,
30
+ de = optimizer.new(objective, ss, {:verbose => verbose,
26
31
  :maxNumSteps => numSteps})
27
32
 
28
33
  start_time = Time.now
@@ -74,34 +79,17 @@ end
74
79
 
75
80
  describe "Easom function" do
76
81
  it 'can optimize the Easom function' do
77
- objective = MinEasom.new
78
- ss = objective.search_space
79
- de = DEOptimizer.new(objective, ss, {:verbose => false,
80
- :maxNumSteps => 30_000, :printFrequency => 0.0,
81
- :samplerRadius => 5})
82
- best = de.optimize().to_a
83
-
84
- val = objective.calc_func(best)
85
- val.must_be_close_to objective.minimum
86
- val.must_be :>=, objective.minimum
87
-
88
- best.must_be_close_to_one_solution_of objective, 0.01
82
+ best, obj, time = best_from_de_on_objective MinBeale.new, nil, 10_000, false, FeldtRuby::Optimize::DEOptimizer_Best_1_Bin
83
+ best.must_be_close_to_one_solution_of obj
84
+ time.must_be :<, 1.5
89
85
  end
90
86
  end
91
87
 
92
88
  describe "EggHolder function" do
93
89
  it 'can optimize the Eggholder function' do
94
- objective = MinEggHolder.new
95
- ss = objective.search_space
96
- de = DEOptimizer.new(objective, ss, {:verbose => false,
97
- :maxNumSteps => 25_000, :samplerRadius => 6})
98
- best = de.optimize().to_a
99
-
100
- val = objective.calc_func(best)
101
- val.must_be_close_to objective.minimum
102
- val.must_be :>=, objective.minimum
103
-
104
- best.must_be_close_to_one_solution_of objective, 0.01
90
+ best, obj, time = best_from_de_on_objective MinEggHolder.new, nil, 10_000, false, FeldtRuby::Optimize::DEOptimizer_Best_1_Bin
91
+ best.must_be_close_to_one_solution_of obj
92
+ time.must_be :<, 1.5
105
93
  end
106
94
  end
107
95
 
@@ -41,4 +41,26 @@ describe "DifferentialEvolution" do
41
41
  de2.best.sum.must_be :<=, 0.40
42
42
  de2.num_optimization_steps.must_equal 1234
43
43
  end
44
+ end
45
+
46
+ describe "DE/best/1/bin" do
47
+ it "works for rms of small vector" do
48
+ s2 = SearchSpace.new_symmetric(2, 1)
49
+ o1 = MinimizeRMS.new
50
+ de1 = DEOptimizer_Best_1_Bin.new(o1, s2, {:verbose => false, :maxNumSteps => 1000})
51
+ de1.optimize()
52
+ # Very unlikely we get a number over 0.30 (2 elements) after 1000 steps...
53
+ de1.best.sum.must_be :<=, 0.30
54
+ de1.num_optimization_steps.must_equal 1000
55
+ end
56
+
57
+ it "works for rms and sum of small vector and with more steps" do
58
+ s4 = SearchSpace.new_symmetric(4, 1)
59
+ o2 = MinimizeRMSAndSum.new
60
+ de2 = DEOptimizer_Best_1_Bin.new(o2, s4, {:verbose => false, :maxNumSteps => 1234})
61
+ de2.optimize()
62
+ # Very unlikely we get a number over 0.40 (4 elements)...
63
+ de2.best.sum.must_be :<=, 0.40
64
+ de2.num_optimization_steps.must_equal 1234
65
+ end
44
66
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feldtruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Feldt