feldtruby 0.3.16 → 0.3.18
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 +7 -0
- data/Gemfile.lock +9 -2
- data/Rakefile +8 -0
- data/feldtruby.gemspec +6 -0
- data/lib/feldtruby/annotations.rb +10 -0
- data/lib/feldtruby/array/basic_stats.rb +3 -1
- data/lib/feldtruby/array/permutations_and_subsets.rb +17 -0
- data/lib/feldtruby/float.rb +23 -0
- data/lib/feldtruby/logger.rb +216 -30
- data/lib/feldtruby/minitest_extensions.rb +0 -1
- data/lib/feldtruby/mongodb.rb +16 -0
- data/lib/feldtruby/mongodb_logger.rb +245 -0
- data/lib/feldtruby/optimize/differential_evolution.rb +29 -5
- data/lib/feldtruby/optimize/elite_archive.rb +91 -0
- data/lib/feldtruby/optimize/max_steps_termination_criterion.rb +1 -1
- data/lib/feldtruby/optimize/objective.rb +343 -222
- data/lib/feldtruby/optimize/optimizer.rb +138 -60
- data/lib/feldtruby/optimize/search_space.rb +10 -0
- data/lib/feldtruby/optimize.rb +1 -26
- data/lib/feldtruby/statistics.rb +74 -3
- data/lib/feldtruby/time.rb +19 -0
- data/lib/feldtruby/version.rb +1 -1
- data/old/event_logger.rb +682 -0
- data/spikes/comparing_samplers_on_classic_optimization_functions/analyze_sampler_comparison_results.R +78 -0
- data/spikes/comparing_samplers_on_classic_optimization_functions/compare_samplers.rb +264 -0
- data/spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_130405_175934.csv +561 -0
- data/spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_levi13_beale_easom_eggholder.csv +11201 -0
- data/spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_levi13_beale_easom_eggholder_all_radii_4_to_30.csv +44801 -0
- data/spikes/comparing_samplers_on_classic_optimization_functions/results_comparing_samplers_omnitest.csv +1401 -0
- data/spikes/mongodb_logger.rb +47 -0
- data/spikes/simple_de_run.rb +32 -0
- data/test/helper.rb +17 -1
- data/test/test_array_basic_stats.rb +5 -1
- data/test/test_array_permutations_and_subsets.rb +23 -0
- data/test/test_float.rb +15 -0
- data/test/test_html_doc_getter.rb +1 -1
- data/test/test_logger.rb +86 -48
- data/test/test_mongodb_logger.rb +116 -0
- data/test/test_object_annotations.rb +14 -0
- data/test/test_optimize.rb +7 -6
- data/test/test_optimize_differential_evolution.rb +21 -19
- data/test/test_optimize_elite_archive.rb +85 -0
- data/test/test_optimize_objective.rb +237 -74
- data/test/test_optimize_populationbasedoptimizer.rb +72 -6
- data/test/test_optimize_random_search.rb +0 -17
- data/test/test_optimize_search_space.rb +15 -0
- data/test/test_statistics.rb +30 -4
- data/test/test_time.rb +22 -0
- data/test/tmp_shorter.csv +200 -0
- metadata +62 -21
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'feldtruby/optimize/optimizer'
|
2
2
|
require 'feldtruby/math/rand'
|
3
3
|
require 'feldtruby/vector'
|
4
|
+
require 'feldtruby/logger'
|
4
5
|
|
5
6
|
module FeldtRuby::Optimize
|
6
7
|
|
7
8
|
# Common to many Evolutionary Computation optimizers
|
8
|
-
class EvolutionaryOptimizer < PopulationBasedOptimizer; end
|
9
|
+
class EvolutionaryOptimizer < FeldtRuby::Optimize::PopulationBasedOptimizer; end
|
9
10
|
|
10
11
|
# Base class for Differential Evolution (DE) for continuous, real-valued optimization.
|
11
12
|
# Since there are many different DE variants this is the base class
|
@@ -57,12 +58,13 @@ class DEOptimizerBase < EvolutionaryOptimizer
|
|
57
58
|
def optimization_step()
|
58
59
|
trial, target, target_index = generate_trial_candidate_and_target()
|
59
60
|
|
60
|
-
# We get [candidate, qualityValue, subQualityValues] for each vector
|
61
61
|
best, worst = objective.rank_candidates([target, trial])
|
62
62
|
|
63
63
|
# Supplant the target vector with the trial vector if better
|
64
|
-
if best
|
65
|
-
|
64
|
+
if best != target
|
65
|
+
logger.log_data :better_candidate_found, {
|
66
|
+
:better => best,
|
67
|
+
:quality => @objective.quality_of(best)}, "Trial vector was better"
|
66
68
|
trial_better = true
|
67
69
|
update_candidate_in_population(target_index, trial)
|
68
70
|
else
|
@@ -72,7 +74,7 @@ class DEOptimizerBase < EvolutionaryOptimizer
|
|
72
74
|
# Give feedback to strategy since some strategies use this to self-adapt
|
73
75
|
feedback_on_trial_vs_target(trial, target, trial_better)
|
74
76
|
|
75
|
-
[best
|
77
|
+
[best]
|
76
78
|
end
|
77
79
|
|
78
80
|
#####################################
|
@@ -183,4 +185,26 @@ class DEOptimizer < DEOptimizerBase
|
|
183
185
|
include DE_MutationStrategy_Rand_1
|
184
186
|
end
|
185
187
|
|
188
|
+
# Optimize the _numVariables_ between the _min_ and _max_ values given _costFunction_.
|
189
|
+
# Default is to minimize.
|
190
|
+
def self.optimize(min, max, options = {:verbose => true},
|
191
|
+
objectiveFuncClass = FeldtRuby::Optimize::ObjectiveMinimizeBlock, &costFunction)
|
192
|
+
objective = objectiveFuncClass.new(&costFunction)
|
193
|
+
num_vars = costFunction.arity
|
194
|
+
search_space = SearchSpace.new_from_min_max(num_vars, min, max)
|
195
|
+
optimizer = DEOptimizer.new(objective, search_space, options)
|
196
|
+
optimizer.optimize()
|
197
|
+
optimizer.best.to_a
|
198
|
+
end
|
199
|
+
|
200
|
+
# Short hand wrapper for function minimization.
|
201
|
+
def self.minimize(min, max, options = {}, &costFunction)
|
202
|
+
optimize(min, max, options, &costFunction)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Short hand wrapper for function maximization.
|
206
|
+
def self.maximize(min, max, options = {}, &costFunction)
|
207
|
+
optimize(min, max, options, FeldtRuby::Optimize::ObjectiveMaximizeBlock, &costFunction)
|
208
|
+
end
|
209
|
+
|
186
210
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'feldtruby/optimize'
|
2
|
+
|
3
|
+
module FeldtRuby::Optimize
|
4
|
+
|
5
|
+
# This keeps a record of the best/elite candidate solutions found during
|
6
|
+
# an optimization search. It keeps separate top lists per goal being optimized
|
7
|
+
# as well as for the aggregate quality value (fitness) itself. The top lists
|
8
|
+
# are all sorted to allow for fast checks and insertion.
|
9
|
+
class EliteArchive
|
10
|
+
DefaultParams = {
|
11
|
+
:NumTopPerGoal => 10,
|
12
|
+
:NumTopAggregate => 25
|
13
|
+
}
|
14
|
+
|
15
|
+
attr_reader :objective, :top_per_goal, :best
|
16
|
+
|
17
|
+
def initialize(objective, options = DefaultParams.clone)
|
18
|
+
@objective = objective
|
19
|
+
@options = options
|
20
|
+
init_top_lists
|
21
|
+
end
|
22
|
+
|
23
|
+
# A top list is an array of a fixed size that saves the top candidates
|
24
|
+
# based on their quality values.
|
25
|
+
class GlobalTopList
|
26
|
+
def initialize(maxSize, objective)
|
27
|
+
@max_size =maxSize
|
28
|
+
@top_list = Array.new
|
29
|
+
@objective = objective
|
30
|
+
end
|
31
|
+
def length; @top_list.length; end
|
32
|
+
|
33
|
+
def [](index)
|
34
|
+
@top_list[index]
|
35
|
+
end
|
36
|
+
|
37
|
+
def add(candidate)
|
38
|
+
last = @top_list.last
|
39
|
+
#puts "In #{self},\nlast = #{last}, candidate = #{candidate}, top_list = #{@top_list}"
|
40
|
+
if @top_list.length < @max_size || last.nil? || is_better_than?(candidate, last)
|
41
|
+
@top_list.pop if @top_list.length >= @max_size
|
42
|
+
@top_list << candidate
|
43
|
+
@top_list = sort_top_list
|
44
|
+
end
|
45
|
+
#puts "top_list = #{@top_list}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def is_better_than?(candidate1, candidate2)
|
49
|
+
@objective.is_better_than?(candidate1, candidate2)
|
50
|
+
end
|
51
|
+
|
52
|
+
def sort_top_list
|
53
|
+
@top_list.sort_by {|c| @objective.quality_of(c).value}
|
54
|
+
end
|
55
|
+
|
56
|
+
def inspect
|
57
|
+
self.class.inspect + @top_list.inspect
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class GoalTopList < GlobalTopList
|
62
|
+
def initialize(maxSize, objective, goalIndex)
|
63
|
+
super(maxSize, objective)
|
64
|
+
@index = goalIndex
|
65
|
+
end
|
66
|
+
def is_better_than?(candidate1, candidate2)
|
67
|
+
@objective.is_better_than_for_goal?(@index, candidate1, candidate2)
|
68
|
+
end
|
69
|
+
def sort_top_list
|
70
|
+
@top_list.sort_by {|c|
|
71
|
+
qv = @objective.quality_of(c)
|
72
|
+
qv.sub_quality(@index, true) # We want the sub quality value posed as a minimization goal regardless of whether it is a min or max goal
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def init_top_lists
|
78
|
+
@top_per_goal = Array.new
|
79
|
+
@objective.num_goals.times do |i|
|
80
|
+
@top_per_goal << GoalTopList.new(@options[:NumTopPerGoal], @objective, i)
|
81
|
+
end
|
82
|
+
@best = GlobalTopList.new(@options[:NumTopAggregate], @objective)
|
83
|
+
end
|
84
|
+
|
85
|
+
def add(candidate)
|
86
|
+
@best.add candidate
|
87
|
+
@top_per_goal.each {|tl| tl.add(candidate)}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -15,7 +15,7 @@ end
|
|
15
15
|
class FeldtRuby::Optimize::MaxStepsTerminationCriterion < FeldtRuby::Optimize::TerminationCriterion
|
16
16
|
attr_accessor :max_steps
|
17
17
|
|
18
|
-
def initialize(maxSteps =
|
18
|
+
def initialize(maxSteps = 10_000)
|
19
19
|
@max_steps = maxSteps
|
20
20
|
end
|
21
21
|
def terminate?(optimizer)
|