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 +4 -4
- data/lib/feldtruby/logger.rb +1 -1
- data/lib/feldtruby/optimize/differential_evolution.rb +37 -28
- data/lib/feldtruby/optimize/objective.rb +0 -3
- data/lib/feldtruby/version.rb +1 -1
- data/test/long_running/single_objective_problems.rb +1 -1
- data/test/long_running/test_single_objective_optimization.rb +13 -25
- data/test/test_optimize_differential_evolution.rb +22 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5558f3b77d1f52c3bb73f77ab79755b9d8e8f20
|
4
|
+
data.tar.gz: 1d6e7c4cea54211d5487b41b71710ea30827ceb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7f2ff3e1edcacd55278c11d228eced97c128ce1628f55f5edb30aee3fdfb6c2a7c676ac62860a62e5cfd33bd588eba795d50f489becbaae592bb63b36f8b07b
|
7
|
+
data.tar.gz: 2397c74ee5a6eea1c250b9481ace101ed6ecc58990649b2fa6d10aaee73136a5b5e758ccd7689c807f1283f49493c7e3ac1987dea11b37fe4fe262f6e8474020
|
data/lib/feldtruby/logger.rb
CHANGED
@@ -9,7 +9,7 @@ module FeldtRuby
|
|
9
9
|
class Logger
|
10
10
|
DefaultParams = {
|
11
11
|
:verbose => false,
|
12
|
-
:printFrequency => 0.
|
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"
|
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
|
-
#
|
146
|
-
module
|
147
|
-
# We need
|
148
|
-
|
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 =
|
160
|
-
p3 + scale_factor(targetIndex) *
|
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
|
-
#
|
165
|
-
|
166
|
-
|
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
|
-
|
169
|
-
|
170
|
-
#
|
171
|
-
def num_parents_to_sample;
|
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
|
-
|
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
|
-
#
|
177
|
-
# Bounding = random
|
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 =
|
181
|
-
class
|
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
|
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},
|
data/lib/feldtruby/version.rb
CHANGED
@@ -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
|
-
|
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 =
|
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
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
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
|