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 +4 -4
- data/lib/evopop/candidate.rb +3 -3
- data/lib/evopop/crossover.rb +45 -0
- data/lib/evopop/population.rb +20 -9
- data/lib/evopop.rb +1 -0
- data/test/test_population.rb +36 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0da75754d1ffbe965685e6298e01c2e121d7f2d9
|
4
|
+
data.tar.gz: 518b5ad7c766dd68d25942b28aa94dab2df0958f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e70e9007decd9653104efc8f6e28f2d8f7a4495588e2def2a1fa31537cb54b31cbf588758ccfd9ddc58556819d0f3b69e20c3cd2de436329785212004b695183
|
7
|
+
data.tar.gz: 8757fab931278ad147efa01cb8c1440431c9c3af430f0c9b13e7a2005ee9bdf61215eb6ad3d79c93c479e2b3702224cb5799f8d77c81146c155592251f81547f
|
data/lib/evopop/candidate.rb
CHANGED
@@ -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
|
data/lib/evopop/population.rb
CHANGED
@@ -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
|
65
|
-
#
|
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
|
-
|
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
data/test/test_population.rb
CHANGED
@@ -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
|
-
|
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.
|
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:
|