feldtruby 0.4.1 → 0.4.2
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/optimize/objective.rb +4 -3
- data/lib/feldtruby/version.rb +1 -1
- data/spikes/comparing_samplers_on_classic_optimization_functions/compare_samplers.rb +0 -16
- data/test/long_running/test_single_objective_optimization.rb +28 -37
- data/test/test_optimize_elite_archive.rb +1 -1
- data/test/test_optimize_objective.rb +57 -14
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a76753a45218658033069292166e1f2a6632958
|
4
|
+
data.tar.gz: f7885cd0eece07094aca1d27da9da38e9ddbf51f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96eef547a76262a695fc9480abf6b672b5536881953754ee5b8461de00fa08df05718c5b3e3e40894d4c4d65f527802cec3f11e695a4ca3e9d5b55ba24b2d9b8
|
7
|
+
data.tar.gz: db581f0014c8252980e263db25eacc6804723022d8b08130678a2cdf0f1711baf1118fbb26e710101e9225b2ff6ea00fb526be987017e2539d6f403acee8d95a
|
@@ -41,7 +41,7 @@ class Objective
|
|
41
41
|
|
42
42
|
attr_reader :global_min_values_per_goal, :global_max_values_per_goal
|
43
43
|
|
44
|
-
def initialize(qualityAggregator = WeightedSumAggregator.new,
|
44
|
+
def initialize(qualityAggregator = MeanWeigthedGlobalRatios.new, #WeightedSumAggregator.new,
|
45
45
|
comparator = LowerAggregateQualityIsBetterComparator.new)
|
46
46
|
|
47
47
|
# A quality aggregator maps the goal values of a candidate to a single number.
|
@@ -360,12 +360,13 @@ end
|
|
360
360
|
# P. J. Bentley and J. P. Wakefield, "Finding Acceptable Solutions in the
|
361
361
|
# Pareto-Optimal Range using Multiobjective Genetic Algorithms", 1997
|
362
362
|
# http://eprints.hud.ac.uk/4052/1/PB_%26_JPW_1997_Finding_Acceptable_Solutions.htm
|
363
|
-
# with the difference that
|
363
|
+
# with the difference that lower values indicate better quality and we use
|
364
|
+
# mean instead of sum, and thus call it MWGR.
|
364
365
|
# It is the weighted sum of the ratios to the best so far for each goal.
|
365
366
|
# One of its benefits is that one need not sort individuals in relation to
|
366
367
|
# their peers; the aggregate fitness value is fully determined by the individual
|
367
368
|
# and the global min and max values for each objective.
|
368
|
-
class Objective::
|
369
|
+
class Objective::MeanWeigthedGlobalRatios < Objective::WeightedSumAggregator
|
369
370
|
def ratio(index, value, min, max)
|
370
371
|
return 1000.0 if value == nil # We heavily penalize if one sub-quality could not be calculated. Max is otherwise 1.0.
|
371
372
|
if objective.is_min_goal?(index)
|
data/lib/feldtruby/version.rb
CHANGED
@@ -32,22 +32,6 @@ SamplerRadiuses = SamplerRadiuses1
|
|
32
32
|
|
33
33
|
NumRepetitionsPerSampler = 5
|
34
34
|
|
35
|
-
# Schwefel 2.22 function as stated in the JADE paper:
|
36
|
-
# http://150.214.190.154/EAMHCO/pdf/JADE.pdf
|
37
|
-
class MinSchwefel2_22 < MinFunctionOfDimension
|
38
|
-
def objective_min_func(x)
|
39
|
-
t1 = x.inject(0.0) do |sum, xi|
|
40
|
-
sum + xi.abs
|
41
|
-
end
|
42
|
-
|
43
|
-
t2 = x.inject(0.0) do |mult, xi|
|
44
|
-
mult * xi.abs
|
45
|
-
end
|
46
|
-
|
47
|
-
t1 + t2
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
35
|
# Schwefel 1.2 function as stated in the JADE paper:
|
52
36
|
# http://150.214.190.154/EAMHCO/pdf/JADE.pdf
|
53
37
|
class MinSchwefel1_2 < MinFunctionOfDimension
|
@@ -20,65 +20,55 @@ module MiniTest::Expectations
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def best_from_de_on_objective(objective, dimensions, numSteps = 25_000, verbose = false)
|
23
|
-
objective.dimensions = dimensions
|
23
|
+
objective.dimensions = dimensions if objective.respond_to?(:dimensions=)
|
24
24
|
ss = objective.search_space
|
25
25
|
de = DEOptimizer.new(objective, ss, {:verbose => verbose,
|
26
26
|
:maxNumSteps => numSteps})
|
27
|
+
|
28
|
+
start_time = Time.now
|
27
29
|
best = de.optimize().to_a
|
30
|
+
elapsed = Time.now - start_time
|
28
31
|
|
29
32
|
val = objective.calc_func(best)
|
30
33
|
val.must_be_close_to objective.minimum
|
31
34
|
val.must_be :>, objective.minimum
|
32
35
|
|
33
|
-
return best, objective
|
36
|
+
return best, objective, elapsed
|
34
37
|
end
|
35
38
|
|
36
39
|
describe "Sphere function" do
|
37
40
|
it 'can optimize the Sphere function in 3 dimensions' do
|
38
|
-
best, obj = best_from_de_on_objective MinSphere.new, 3, 12_000
|
41
|
+
best, obj, time = best_from_de_on_objective MinSphere.new, 3, 12_000
|
42
|
+
best.must_be_close_to_one_solution_of obj
|
43
|
+
time.must_be :<, 1.5
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'can optimize the Sphere function in 10 dimensions' do
|
47
|
+
best, obj, time = best_from_de_on_objective MinSphere.new, 10, 60_000
|
39
48
|
best.must_be_close_to_one_solution_of obj
|
49
|
+
time.must_be :<, 7.5
|
40
50
|
end
|
41
51
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
#
|
46
|
-
|
47
|
-
# it 'can optimize the Sphere function in 30 dimensions' do
|
48
|
-
# best, obj = best_from_de_on_objective MinSphere.new, 30, 220_000
|
49
|
-
# # We don't test closeness since it might take very long for 30D to get close on all dimensions.
|
50
|
-
# end
|
52
|
+
it 'can optimize the Sphere function in 30 dimensions' do
|
53
|
+
best, obj, time = best_from_de_on_objective MinSphere.new, 30, 220_000
|
54
|
+
time.must_be :<, 28.0
|
55
|
+
# We don't test closeness since it might take very long for 30D to get close on all dimensions.
|
56
|
+
end
|
51
57
|
end
|
52
58
|
|
53
59
|
describe "Levi13 function" do
|
54
60
|
it 'can optimize the Levi13 function' do
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
:maxNumSteps => 7_500})
|
59
|
-
best = de.optimize().to_a
|
60
|
-
|
61
|
-
val = objective.calc_func(best)
|
62
|
-
val.must_be_close_to objective.minimum
|
63
|
-
val.must_be :>=, objective.minimum
|
64
|
-
|
65
|
-
best.must_be_close_to_one_solution_of objective, 0.01
|
61
|
+
best, obj, time = best_from_de_on_objective MinLevi13.new, nil, 7_500
|
62
|
+
best.must_be_close_to_one_solution_of obj
|
63
|
+
time.must_be :<, 1.0
|
66
64
|
end
|
67
65
|
end
|
68
66
|
|
69
67
|
describe "Beale function" do
|
70
68
|
it 'can optimize the Beale function' do
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
:maxNumSteps => 7_500})
|
75
|
-
best = de.optimize().to_a
|
76
|
-
|
77
|
-
val = objective.calc_func(best)
|
78
|
-
val.must_be_close_to objective.minimum
|
79
|
-
val.must_be :>=, objective.minimum
|
80
|
-
|
81
|
-
best.must_be_close_to_one_solution_of objective, 0.01
|
69
|
+
best, obj, time = best_from_de_on_objective MinBeale.new, nil, 7_500
|
70
|
+
best.must_be_close_to_one_solution_of obj
|
71
|
+
time.must_be :<, 1.0
|
82
72
|
end
|
83
73
|
end
|
84
74
|
|
@@ -88,7 +78,7 @@ describe "Easom function" do
|
|
88
78
|
ss = objective.search_space
|
89
79
|
# Why can't we do this in 25_000 evals anymore? We did it before. Repeatedly. Very strange.
|
90
80
|
de = DEOptimizer.new(objective, ss, {:verbose => false,
|
91
|
-
:maxNumSteps =>
|
81
|
+
:maxNumSteps => 40_000, :printFrequency => 0.0,
|
92
82
|
:samplerRadius => 5})
|
93
83
|
best = de.optimize().to_a
|
94
84
|
|
@@ -104,7 +94,7 @@ describe "EggHolder function" do
|
|
104
94
|
it 'can optimize the Eggholder function' do
|
105
95
|
objective = MinEggHolder.new
|
106
96
|
ss = objective.search_space
|
107
|
-
de = DEOptimizer.new(objective, ss, {:verbose =>
|
97
|
+
de = DEOptimizer.new(objective, ss, {:verbose => false,
|
108
98
|
:maxNumSteps => 25_000, :samplerRadius => 6})
|
109
99
|
best = de.optimize().to_a
|
110
100
|
|
@@ -118,7 +108,8 @@ end
|
|
118
108
|
|
119
109
|
describe "Schwefel 2.22 function" do
|
120
110
|
it 'can optimize the Schwefel 2.22 function in 3 dimensions' do
|
121
|
-
best, obj = best_from_de_on_objective MinSchwefel2_22.new, 3, 12_000
|
111
|
+
best, obj, time = best_from_de_on_objective MinSchwefel2_22.new, 3, 12_000
|
122
112
|
best.must_be_close_to_one_solution_of obj
|
113
|
+
time.must_be :<, 1.5
|
123
114
|
end
|
124
115
|
end
|
@@ -16,7 +16,7 @@ end
|
|
16
16
|
|
17
17
|
describe "EliteArchive" do
|
18
18
|
before do
|
19
|
-
@o = TwoMinOneMax.new
|
19
|
+
@o = TwoMinOneMax.new(FeldtRuby::Optimize::Objective::WeightedSumAggregator.new)
|
20
20
|
@a = FeldtRuby::Optimize::EliteArchive.new(@o, {
|
21
21
|
:NumTopPerGoal => 2,
|
22
22
|
:NumTopAggregate => 3})
|
@@ -12,7 +12,7 @@ end
|
|
12
12
|
|
13
13
|
describe "a single minimizing objective" do
|
14
14
|
before do
|
15
|
-
@o = SingleObjective1.new
|
15
|
+
@o = SingleObjective1.new(FeldtRuby::Optimize::Objective::WeightedSumAggregator.new)
|
16
16
|
end
|
17
17
|
|
18
18
|
it "has one goal" do
|
@@ -162,7 +162,7 @@ end
|
|
162
162
|
|
163
163
|
describe "two sub-objectives" do
|
164
164
|
before do
|
165
|
-
@o = TwoMinObjectives1.new
|
165
|
+
@o = TwoMinObjectives1.new(FeldtRuby::Optimize::Objective::WeightedSumAggregator.new)
|
166
166
|
end
|
167
167
|
|
168
168
|
it "has two aspects/sub-objectives" do
|
@@ -291,8 +291,8 @@ end
|
|
291
291
|
|
292
292
|
describe "the objective itself and its updates" do
|
293
293
|
before do
|
294
|
-
@o = SingleObjective1.new
|
295
|
-
@o2 = TwoMinObjectives1.new
|
294
|
+
@o = SingleObjective1.new(FeldtRuby::Optimize::Objective::WeightedSumAggregator.new)
|
295
|
+
@o2 = TwoMinObjectives1.new(FeldtRuby::Optimize::Objective::WeightedSumAggregator.new)
|
296
296
|
@c = [1,2,3]
|
297
297
|
end
|
298
298
|
|
@@ -383,7 +383,7 @@ end
|
|
383
383
|
|
384
384
|
describe "calculating quality when there is one max goal" do
|
385
385
|
before do
|
386
|
-
@o = OneMinOneMaxObjective1.new
|
386
|
+
@o = OneMinOneMaxObjective1.new(FeldtRuby::Optimize::Objective::WeightedSumAggregator.new)
|
387
387
|
end
|
388
388
|
|
389
389
|
it "inverts the max value so that it grows downwards" do
|
@@ -395,43 +395,86 @@ end
|
|
395
395
|
|
396
396
|
describe "calculating quality with weights" do
|
397
397
|
before do
|
398
|
-
@o = OneMinOneMaxObjective1.new
|
398
|
+
@o = OneMinOneMaxObjective1.new(FeldtRuby::Optimize::Objective::WeightedSumAggregator.new)
|
399
399
|
@o.weights = {:objective_min_distance_between => 2, :objective_max_sum => 3}
|
400
400
|
end
|
401
401
|
|
402
|
-
it "uses the weights when calculating quality" do
|
402
|
+
it "uses the weights when calculating quality, even after weights change" do
|
403
403
|
i1 = [1,2,3]
|
404
|
-
@o.weights = {:objective_min_distance_between => 2, :objective_max_sum => 3}
|
405
404
|
q1 = @o.quality_of(i1)
|
406
|
-
q1.value.must_equal( 2*((2-1) + (3-2)) + (-3)*(1+2+3) )
|
407
405
|
q1.sub_qualities.must_equal [2.0, 6.0]
|
406
|
+
q1.value.must_equal( 2*((2-1) + (3-2)) + (-3)*(1+2+3) )
|
408
407
|
q1.candidate.must_equal i1
|
409
408
|
q1.objective.must_equal @o
|
410
409
|
|
411
410
|
@o.weights = {:objective_min_distance_between => 5, :objective_max_sum => 30}
|
412
411
|
q2 = @o.quality_of(i1)
|
413
|
-
q2.value.must_equal( 5*((2-1) + (3-2)) + (-30)*(1+2+3) )
|
414
412
|
q2.sub_qualities.must_equal [2.0, 6.0]
|
413
|
+
q2.value.must_equal( 5*((2-1) + (3-2)) + (-30)*(1+2+3) )
|
414
|
+
|
415
|
+
i2 = [1,2,5]
|
416
|
+
q3 = @o.quality_of i2
|
417
|
+
q3.sub_qualities.must_equal [4.0, 8.0]
|
418
|
+
q3.value.must_equal( 5*4.0 + (-30)*8.0 )
|
419
|
+
|
420
|
+
@o.weights = {:objective_min_distance_between => -10, :objective_max_sum => 1}
|
421
|
+
q3.value.must_equal( (-10)*4.0 + (-1)*8.0 )
|
422
|
+
q2.value.must_equal( (-10)*2.0 + (-1)*6.0 )
|
423
|
+
q1.value.must_equal( (-10)*2.0 + (-1)*6.0 )
|
424
|
+
|
425
|
+
@o.weights = {:objective_min_distance_between => -4, :objective_max_sum => -2}
|
426
|
+
q3.value.must_equal( (-4)*4.0 + (2)*8.0 ) # 0.0
|
427
|
+
q2.value.must_equal( (-4)*2.0 + (2)*6.0 ) # 4.0
|
428
|
+
q1.value.must_equal( (-4)*2.0 + (2)*6.0 )
|
429
|
+
|
430
|
+
i3 = [1,2,3,4,5,6]
|
431
|
+
q4 = @o.quality_of i3
|
432
|
+
q4.sub_qualities.must_equal [5.0, 21.0]
|
433
|
+
q4.value.must_equal( (-4)*5.0 + (2)*21.0 ) # 22.0
|
434
|
+
|
435
|
+
@o.rank_candidates([i1, i2, i3]).must_equal [i2, i1, i3]
|
436
|
+
|
437
|
+
@o.weights = {:objective_min_distance_between => 1, :objective_max_sum => 100}
|
438
|
+
@o.rank_candidates([i1, i2, i3]).must_equal [i3, i2, i1]
|
439
|
+
|
440
|
+
@o.weights = {:objective_min_distance_between => 100, :objective_max_sum => 1}
|
441
|
+
@o.rank_candidates([i1, i2, i3]).must_equal [i1, i2, i3]
|
415
442
|
end
|
416
443
|
end
|
417
444
|
|
418
445
|
describe "Using MWGR for range-independent aggregate fitness calc" do
|
419
446
|
before do
|
420
|
-
@qa = FeldtRuby::Optimize::Objective::
|
447
|
+
@qa = FeldtRuby::Optimize::Objective::MeanWeigthedGlobalRatios.new
|
421
448
|
@o = OneMinOneMaxObjective1.new(@qa)
|
422
449
|
end
|
423
450
|
|
424
451
|
it 'works for a simple scenario' do
|
425
|
-
|
452
|
+
i1 = [1,2]
|
453
|
+
q1 = @o.quality_of(i1) # [1, 3] => 0.0
|
426
454
|
q1.value.must_equal 0.0 # First eval must give perfect score since scales are tight...
|
427
455
|
|
428
|
-
|
456
|
+
i2 = [1,3]
|
457
|
+
q2 = @o.quality_of(i2) # [2, 4] => 0.5
|
429
458
|
q2.value.must_equal 0.5 # Perfect on one (max sum) and worst on other (min distance) so (0+1.0)/2
|
430
459
|
q1.value.must_equal 0.5 # Perfect on one (min distance) and worst on other (max sum) so (1.0+0.0)/2
|
431
460
|
|
432
|
-
|
461
|
+
i3 = [1,4]
|
462
|
+
q3 = @o.quality_of(i3) # [3, 5] => (0+1.0)/2
|
433
463
|
q3.value.must_equal 0.5 # Perfect on one (max sum) and worst on other (min distance) so (0+1.0)/2
|
434
464
|
q2.value.must_equal( ((2.0-1.0)/(3.0-1.0) + ((5.0-4.0)/(5.0-3.0)))/2.0 )
|
435
465
|
q1.value.must_equal( ((1.0-1.0)/(3.0-1.0) + ((5.0-3.0)/(5.0-3.0)))/2.0 )
|
466
|
+
|
467
|
+
i4 = [1,2,3]
|
468
|
+
q4 = @o.quality_of(i4) # [2, 6]
|
469
|
+
q4.value.must_equal( ((2.0-1.0)/(3.0-1.0) + ((6.0-6.0)/(6.0-3.0)))/2.0 )
|
470
|
+
q3.value.must_equal( ((3.0-1.0)/(3.0-1.0) + ((6.0-5.0)/(6.0-3.0)))/2.0 )
|
471
|
+
q2.value.must_equal( ((2.0-1.0)/(3.0-1.0) + ((6.0-4.0)/(6.0-3.0)))/2.0 )
|
472
|
+
q1.value.must_equal( ((1.0-1.0)/(3.0-1.0) + ((6.0-3.0)/(6.0-3.0)))/2.0 )
|
473
|
+
|
474
|
+
@o.rank_candidates([i1,i3,i4]).must_equal [i4,i1,i3]
|
475
|
+
@o.rank_candidates([i3,i4,i2]).must_equal [i4,i2,i3]
|
476
|
+
@o.rank_candidates([i3,i4]).must_equal [i4,i3]
|
477
|
+
@o.rank_candidates([i3,i1]).must_equal [i1,i3]
|
478
|
+
@o.rank_candidates([i2,i3]).must_equal [i2,i3]
|
436
479
|
end
|
437
480
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feldtruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Feldt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-04-
|
11
|
+
date: 2013-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rinruby
|