evopop 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 43f55266a4ef8f115db03cc3dd2f075ce4189110
4
- data.tar.gz: e66bcb2525067d93ae87c8c3355c5a1f32030d05
3
+ metadata.gz: 0da75754d1ffbe965685e6298e01c2e121d7f2d9
4
+ data.tar.gz: 518b5ad7c766dd68d25942b28aa94dab2df0958f
5
5
  SHA512:
6
- metadata.gz: 5e9fda825a2389dde2ef427cbd2d8d283e8d03ca42227d8266a99244d01799d782a2ca9cd51615cbcf4687e01796c891125951e8654c549c40b5485c7be5d391
7
- data.tar.gz: 18c4d946acf6c631db4784d10177ec01762f8a16524bbe794b1cdf97fb62002680c251fd939fa9c3a072182729555fb163055527c11ffd4e8a10d498aaf15880
6
+ metadata.gz: e70e9007decd9653104efc8f6e28f2d8f7a4495588e2def2a1fa31537cb54b31cbf588758ccfd9ddc58556819d0f3b69e20c3cd2de436329785212004b695183
7
+ data.tar.gz: 8757fab931278ad147efa01cb8c1440431c9c3af430f0c9b13e7a2005ee9bdf61215eb6ad3d79c93c479e2b3702224cb5799f8d77c81146c155592251f81547f
@@ -5,7 +5,7 @@ class Candidate
5
5
  attr_accessor :dna, :fitness
6
6
 
7
7
  # Simple initialization of candidate object.
8
- def initialize
9
- @dna = []
8
+ def initialize(dna=[])
9
+ @dna = dna
10
10
  end
11
- end
11
+ end
@@ -0,0 +1,45 @@
1
+ # Represents a collection of well known crossover functions.
2
+ #
3
+ module Crossover
4
+
5
+ # Perform 1 point crossover for a pair of candidates at the ordinal.
6
+ # http://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)#One-point_crossover
7
+ def self.one_point(candidates, params)
8
+ ordinal = params[:ordinal]
9
+
10
+ # Compose the dna of the first child from the first chunk of the
11
+ # first candidate and the second chunk of the second candidate
12
+ dna0_left = candidates[0].dna.take(ordinal)
13
+ dna1_right = candidates[1].dna.drop(ordinal)
14
+
15
+ # Compose the dna of the second child from the first chunk of the
16
+ # first candidate and the second chunk of the second candidate
17
+ dna1_left = candidates[1].dna.take(ordinal)
18
+ dna0_right = candidates[0].dna.drop(ordinal)
19
+
20
+ # Initialize and assign DNA to children.
21
+ children = [Candidate.new(dna = dna0_left + dna1_right),
22
+ Candidate.new(dna = dna1_left + dna0_right)]
23
+
24
+ return children
25
+ end
26
+
27
+ # Perform n_point crossover for a pair of candidates. Will output two children from the n_point crossover.
28
+ #
29
+ # Example:
30
+ # n_point
31
+ def self.n_point(candidates, params)
32
+
33
+ end
34
+
35
+ def self.average(candidates, params)
36
+ child = Candidate.new
37
+ dna_length = candidates[0].dna.length
38
+ (0...dna_length).each { |j|
39
+ child.dna << (candidates[0].dna[j] + candidates[1].dna[j])/2.0 # Initialize the dna of the child with the average of the parents' dna.
40
+ }
41
+
42
+ return [child]
43
+ end
44
+
45
+ end
@@ -10,7 +10,7 @@
10
10
  # population.crossover
11
11
  # population.mutate
12
12
  class Population
13
- attr_accessor :candidates, :population_size, :max_generations, :initial_range_min, :initial_range_max, :mutation_range_min, :mutation_range_max, :mutation_num, :fitness_function, :dna_len, :average_fitness
13
+ attr_accessor :candidates, :population_size, :max_generations, :crossover_function, :crossover_params, :initial_range_min, :initial_range_max, :mutation_range_min, :mutation_range_max, :mutation_num, :fitness_function, :dna_len, :average_fitness
14
14
 
15
15
  # Initializes the attributes with default values. This is not guaranteed
16
16
  # to reach maxima.
@@ -24,7 +24,9 @@ class Population
24
24
  @mutation_range_max = 10
25
25
  @mutation_num = (0.10*@population_size).to_i
26
26
  @dna_len = 1
27
+ @crossover_params = {:ordinal => (@dna_len/2)}
27
28
 
29
+ @crossover_function = Crossover.method(:one_point)
28
30
  @fitness_function = Proc.new { |dna|
29
31
  Math.sin(dna[0])
30
32
  }
@@ -61,22 +63,31 @@ class Population
61
63
  end
62
64
 
63
65
  # Performs simple mechanism of crossover - in this case picks two
64
- # random candidates in from a top percentile of the population and averages
65
- # out their DNA per dimension, producing new offspring equal to the
66
+ # random candidates in from a top percentile of the population and
67
+ # performs one point crossover, producing new offspring equal to the
66
68
  # population size attribute.
67
69
  def crossover
70
+ # Define the candidates that can have children.
68
71
  @candidates = @candidates.take((@population_size*0.75).to_i)
69
72
 
70
73
  new_generation = Array.new
74
+
71
75
  (0...@population_size).each {|i|
76
+ # For each of the top 75% of the population take 2
72
77
  couple = @candidates.sample(2)
73
- child = Candidate.new
74
- (0...@dna_len).each {|j|
75
- child.dna << (couple[0].dna[j] + couple[1].dna[j])/2.0 # Initialize the dna of the child with the average of the parents' dna.
76
- }
77
- new_generation << child
78
- }
78
+ params = @crossover_params
79
79
 
80
+ children = @crossover_function.call(couple, params)
81
+
82
+ new_generation = new_generation + children
83
+
84
+ # When we go above set population_size, take the first population_size
85
+ # candidates, ignore the rest.
86
+ if new_generation.length >= self.population_size
87
+ new_generation = new_generation.take(self.population_size)
88
+ break
89
+ end
90
+ }
80
91
  @candidates = new_generation
81
92
  end
82
93
 
data/lib/evopop.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'evopop/population'
2
2
  require 'evopop/candidate'
3
+ require 'evopop/crossover'
3
4
 
4
5
  class Evopop
5
6
 
@@ -14,6 +14,8 @@ class PopulationTest < Test::Unit::TestCase
14
14
  population.mutation_range_min = -100.0
15
15
  population.mutation_range_max = 100.0
16
16
  population.mutation_num = 10
17
+ population.crossover_params = {:ordinal => (population.dna_len/2)}
18
+ population.crossover_function = Crossover.method(:one_point)
17
19
  population.fitness_function = Proc.new { |dna|
18
20
  Math.sin(dna[0]) + Math.cos(dna[1])
19
21
  }
@@ -38,6 +40,10 @@ class PopulationTest < Test::Unit::TestCase
38
40
  }
39
41
  end
40
42
 
43
+ # Simple test of the training function. Ensure that when training
44
+ # finishes the fitness of the ith element of the population is
45
+ # less than or equal to the i-1th element of the population. I.e.
46
+ # fitness is becoming greater over the iteration of the popoulation.
41
47
  def test_train
42
48
  # Arrange: Initialize the population
43
49
  population = initialize_population
@@ -55,6 +61,8 @@ class PopulationTest < Test::Unit::TestCase
55
61
  }
56
62
  end
57
63
 
64
+ # Simple test to ensure that only the exact number of candidates in the
65
+ # population are mutated.
58
66
  def test_mutation
59
67
  # Arrange: Initialize the population
60
68
  population = initialize_population
@@ -76,10 +84,37 @@ class PopulationTest < Test::Unit::TestCase
76
84
  assert_equal(population.mutation_num, counter)
77
85
  end
78
86
 
79
- def test_cross_over
87
+ # Simple
88
+ def test_one_point_crossover
80
89
  # Arrange: Initialize the population
81
90
  population = initialize_population
91
+
92
+ # Act: Train and corssover the population a number of times
93
+ 5.times {
94
+ population.train
95
+ population.crossover
96
+ }
82
97
 
98
+ # Assert: The initial average fitness is less than what occurs after 100 generations.
99
+ # This is to ensure that over generations the average fitness does indeed go up, given
100
+ # no mutation.
101
+ assert_equal(true, population.average_fitness[0] < population.average_fitness[population.average_fitness.length-1])
83
102
  end
84
103
 
104
+ def test_average_crossover
105
+ # Arrange: Initialize the population
106
+ population = initialize_population
107
+ population.crossover_function = Crossover.method(:average)
108
+
109
+ # Act: Train and corssover the population a number of times
110
+ 100.times {
111
+ population.train
112
+ population.crossover
113
+ }
114
+
115
+ # Assert: The initial average fitness is less than what occurs after 100 generations.
116
+ # This is to ensure that over generations the average fitness does indeed go up, given
117
+ # no mutation.
118
+ assert_equal(true, population.average_fitness[0] < population.average_fitness[population.average_fitness.length-1])
119
+ end
85
120
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evopop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elvin Lucero
@@ -19,6 +19,7 @@ files:
19
19
  - lib/evopop.rb
20
20
  - lib/evopop/population.rb
21
21
  - lib/evopop/candidate.rb
22
+ - lib/evopop/crossover.rb
22
23
  - test/test_population.rb
23
24
  homepage: https://github.com/elvinlucero/evopop
24
25
  licenses: