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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0aea61457b6e6c7b8258675bed056af881761d67
4
- data.tar.gz: b726de556b3d1c6e5471f88433bf0117847400e8
3
+ metadata.gz: 3a76753a45218658033069292166e1f2a6632958
4
+ data.tar.gz: f7885cd0eece07094aca1d27da9da38e9ddbf51f
5
5
  SHA512:
6
- metadata.gz: dd59de72274a8ad60e97f0753e13449b77c1f4da793a78a20b16103e36f01132916f1f4616638a5430868877e14fd7ae4b87d8f6fc33df1072c85aab9fd273fa
7
- data.tar.gz: 67cb9c8b514e6d4139842fb0750854687a952957f1a8650c33173fc068f93d189ffcb23821d3bed0f3cf0704eb29b570c6e4ac5d781ed9a4445a17cd463d5e58
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 that lower values indicate better quality.
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::SumOfWeigthedGlobalRatios < Objective::WeightedSumAggregator
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)
@@ -1,3 +1,3 @@
1
1
  module FeldtRuby
2
- VERSION = "0.4.1"
2
+ VERSION = "0.4.2"
3
3
  end
@@ -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
- # it 'can optimize the Sphere function in 10 dimensions' do
43
- # best, obj = best_from_de_on_objective MinSphere.new, 10, 60_000
44
- # best.must_be_close_to_one_solution_of obj
45
- # end
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
- objective = MinLevi13.new
56
- ss = objective.search_space
57
- de = DEOptimizer.new(objective, ss, {:verbose => false,
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
- objective = MinBeale.new
72
- ss = objective.search_space
73
- de = DEOptimizer.new(objective, ss, {:verbose => false,
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 => 35_000, :printFrequency => 0.0,
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 => true,
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::SumOfWeigthedGlobalRatios.new
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
- q1 = @o.quality_of([1,2]) # [1, 3] => 0.0
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
- q2 = @o.quality_of([1,3]) # [2, 4] => 0.5
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
- q3 = @o.quality_of([1,4]) # [3, 5] => (0+1.0)/2
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.1
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-06 00:00:00.000000000 Z
11
+ date: 2013-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rinruby