feldtruby 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|