evopop 0.0.4 → 0.0.5
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/LICENSE +20 -0
- data/README.md +42 -0
- data/lib/evopop/candidate.rb +3 -3
- data/lib/evopop/crossover.rb +72 -16
- data/lib/evopop/population.rb +37 -33
- data/lib/evopop.rb +3 -10
- metadata +12 -12
- data/test/test_population.rb +0 -120
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04d8cc00d713a77a686106e3be50d26d95889ec0
|
4
|
+
data.tar.gz: 0ee8bc2bca81622f24aa80184fa942fce901064d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2cae28bdfd5bc8d7e9aa431bfd2ff5765cbe7c5e4a7e2e1f28fb07269b8b42daf90e0554c772dfbe060735c3a278937bdf59752a192c1e623dacce55fd51679f
|
7
|
+
data.tar.gz: 6d418afe3bf3d4ec71e1e0f10d6877528fba2206759672d6299b27957e3d4f7e508740c5717fb13fab832933d4ebde93dbadf20849f52198950153f189ad9600
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Elvin Lucero
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
Evopop
|
2
|
+
------------------------
|
3
|
+
|
4
|
+
This is a library for implementing simple genetic algorithms to evolve over a fitness function.
|
5
|
+
|
6
|
+
|
7
|
+
``` ruby
|
8
|
+
|
9
|
+
require 'evopop'
|
10
|
+
|
11
|
+
# Initialize the population to be trained with good defaults.
|
12
|
+
population = Population.new
|
13
|
+
population.population_size = 1000
|
14
|
+
population.dna_len = 2
|
15
|
+
population.max_generations = 1000
|
16
|
+
population.initial_range_min = -10_000.0
|
17
|
+
population.initial_range_max = 10_000.0
|
18
|
+
population.mutation_range_min = -10.0
|
19
|
+
population.mutation_range_max = 10.0
|
20
|
+
population.mutation_num = 10
|
21
|
+
population.crossover_params = { ordinal: (DNA_LEN / 2) }.freeze
|
22
|
+
population.crossover_function = Crossover.method(:one_point)
|
23
|
+
population.fitness_function = proc do |dna|
|
24
|
+
Math.sin(dna[0]) + Math.cos(dna[1])
|
25
|
+
end
|
26
|
+
|
27
|
+
# Initialize the population
|
28
|
+
population.create
|
29
|
+
|
30
|
+
# Train a population for population.max_generations.
|
31
|
+
(0...population.max_generations).each do |i|
|
32
|
+
population.train
|
33
|
+
population.crossover
|
34
|
+
population.mutate if i != population.max_generations - 1
|
35
|
+
end
|
36
|
+
|
37
|
+
# Sort and print out candidate with highest fitness in the last generation.
|
38
|
+
population.train
|
39
|
+
puts "Finished #{population.max_generations} generations with the fittest candidate with a dna of #{population.candidates[0].dna} and a fitness of #{population.candidates[0].fitness}."
|
40
|
+
|
41
|
+
|
42
|
+
```
|
data/lib/evopop/candidate.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# Public: Represents a candidate in the population. Candidates are abstracted
|
2
|
-
# as a simple data structure which contains the DNA and fitness over the
|
2
|
+
# as a simple data structure which contains the DNA and fitness over the
|
3
3
|
# fitness function.
|
4
4
|
class Candidate
|
5
5
|
attr_accessor :dna, :fitness
|
6
6
|
|
7
7
|
# Simple initialization of candidate object.
|
8
|
-
def initialize(dna=[])
|
8
|
+
def initialize(dna = [])
|
9
9
|
@dna = dna
|
10
10
|
end
|
11
|
-
end
|
11
|
+
end
|
data/lib/evopop/crossover.rb
CHANGED
@@ -1,45 +1,101 @@
|
|
1
1
|
# Represents a collection of well known crossover functions.
|
2
|
-
#
|
2
|
+
#
|
3
3
|
module Crossover
|
4
|
-
|
5
4
|
# Perform 1 point crossover for a pair of candidates at the ordinal.
|
6
5
|
# http://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)#One-point_crossover
|
7
6
|
def self.one_point(candidates, params)
|
8
7
|
ordinal = params[:ordinal]
|
9
8
|
|
10
|
-
# Compose the dna of the first child from the first chunk of the
|
9
|
+
# Compose the dna of the first child from the first chunk of the
|
11
10
|
# first candidate and the second chunk of the second candidate
|
12
11
|
dna0_left = candidates[0].dna.take(ordinal)
|
13
12
|
dna1_right = candidates[1].dna.drop(ordinal)
|
14
|
-
|
15
|
-
# Compose the dna of the second child from the first chunk of the
|
13
|
+
|
14
|
+
# Compose the dna of the second child from the first chunk of the
|
16
15
|
# first candidate and the second chunk of the second candidate
|
17
16
|
dna1_left = candidates[1].dna.take(ordinal)
|
18
17
|
dna0_right = candidates[0].dna.drop(ordinal)
|
19
18
|
|
20
19
|
# Initialize and assign DNA to children.
|
21
|
-
children = [Candidate.new(
|
22
|
-
Candidate.new(
|
20
|
+
children = [Candidate.new(dna0_left + dna1_right),
|
21
|
+
Candidate.new(dna1_left + dna0_right)]
|
22
|
+
|
23
|
+
children
|
24
|
+
end
|
25
|
+
|
26
|
+
# Perform two point crossover over a pair of candidates. Will output two
|
27
|
+
# children with genes spliced over the crossover points.
|
23
28
|
|
24
|
-
|
29
|
+
def self.two_point(candidates, params)
|
30
|
+
# Ordinals should be stored in params as a comma separated list. I.e. "1,2".
|
31
|
+
# Make sure to sort.
|
32
|
+
ordinals = params[:ordinals].split(',').sort.collect(&:to_i)
|
33
|
+
|
34
|
+
# Initialize and assign the DNA of the children.
|
35
|
+
cdna0 = candidates[0].dna
|
36
|
+
cdna1 = candidates[1].dna
|
37
|
+
|
38
|
+
children = [
|
39
|
+
Candidate.new(cdna0[0..ordinals[0]] + cdna1[(ordinals[0] + 1)..ordinals[1]] + cdna0[(ordinals[1] + 1)..cdna0.length - 1]),
|
40
|
+
Candidate.new(cdna1[0..ordinals[0]] + cdna0[(ordinals[0] + 1)..ordinals[1]] + cdna1[(ordinals[1] + 1)..cdna1.length - 1])
|
41
|
+
]
|
42
|
+
|
43
|
+
children
|
25
44
|
end
|
26
45
|
|
27
46
|
# Perform n_point crossover for a pair of candidates. Will output two children from the n_point crossover.
|
28
|
-
#
|
47
|
+
#
|
29
48
|
# Example:
|
30
49
|
# n_point
|
31
50
|
def self.n_point(candidates, params)
|
51
|
+
ordinals = params[:ordinals].split(',').sort.collect(&:to_i)
|
32
52
|
|
53
|
+
pdna0 = candidates[0].dna
|
54
|
+
pdna1 = candidates[1].dna
|
55
|
+
|
56
|
+
dna_length = candidates[0].dna.length
|
57
|
+
|
58
|
+
cdna0 = []
|
59
|
+
cdna1 = []
|
60
|
+
|
61
|
+
old_ordinal = 0
|
62
|
+
synchronous = ordinals[0] == 0 ? false : true
|
63
|
+
|
64
|
+
ordinals.each do |i|
|
65
|
+
if synchronous
|
66
|
+
cdna0 += pdna0[old_ordinal..i]
|
67
|
+
cdna1 += pdna1[old_ordinal..i]
|
68
|
+
else
|
69
|
+
cdna0 += pdna1[old_ordinal..i]
|
70
|
+
cdna1 += pdna0[old_ordinal..i]
|
71
|
+
end
|
72
|
+
|
73
|
+
synchronous = !synchronous
|
74
|
+
old_ordinal = i + 1
|
75
|
+
|
76
|
+
next if ordinals.last != old_ordinal - 1
|
77
|
+
|
78
|
+
if synchronous
|
79
|
+
cdna0 += pdna0[old_ordinal..dna_length - 1]
|
80
|
+
cdna1 += pdna1[old_ordinal..dna_length - 1]
|
81
|
+
else
|
82
|
+
cdna0 += pdna1[old_ordinal..dna_length - 1]
|
83
|
+
cdna1 += pdna0[old_ordinal..dna_length - 1]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
[Candidate.new(cdna0), Candidate.new(cdna1)]
|
33
88
|
end
|
34
89
|
|
35
|
-
def self.average(candidates,
|
90
|
+
def self.average(candidates, _params)
|
36
91
|
child = Candidate.new
|
37
92
|
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
93
|
|
42
|
-
|
43
|
-
|
94
|
+
(0...dna_length).each do |j|
|
95
|
+
# Initialize the dna of the child with the average of the parents' dna.
|
96
|
+
child.dna << (candidates[0].dna[j] + candidates[1].dna[j]) / 2.0
|
97
|
+
end
|
44
98
|
|
45
|
-
|
99
|
+
[child]
|
100
|
+
end
|
101
|
+
end
|
data/lib/evopop/population.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
# Represents the population that is being trained. Has various methods
|
3
3
|
# relevant to training.
|
4
|
-
#
|
4
|
+
#
|
5
5
|
#
|
6
6
|
# Examples
|
7
7
|
# population = Population.new
|
@@ -22,85 +22,89 @@ class Population
|
|
22
22
|
@initial_range_max = 100
|
23
23
|
@mutation_range_min = -10
|
24
24
|
@mutation_range_max = 10
|
25
|
-
@mutation_num = (0.10
|
25
|
+
@mutation_num = (0.10 * @population_size).to_i
|
26
26
|
@dna_len = 1
|
27
|
-
@crossover_params = {:
|
27
|
+
@crossover_params = { ordinal: (@dna_len / 2) }
|
28
28
|
|
29
29
|
@crossover_function = Crossover.method(:one_point)
|
30
|
-
@fitness_function =
|
30
|
+
@fitness_function = proc do |dna|
|
31
31
|
Math.sin(dna[0])
|
32
|
-
|
32
|
+
end
|
33
|
+
|
34
|
+
create
|
33
35
|
|
34
|
-
self
|
36
|
+
self
|
35
37
|
end
|
36
38
|
|
37
39
|
# Creates a new population class. Should be called after all the
|
38
40
|
# parameters have been set to the attributes.
|
39
41
|
def create
|
40
|
-
@candidates = Array.new(@population_size)
|
42
|
+
@candidates = Array.new(@population_size) do
|
41
43
|
candidate = Candidate.new
|
42
|
-
(0...@dna_len).each
|
44
|
+
(0...@dna_len).each do
|
43
45
|
candidate.dna << Random.rand(@initial_range_min...@initial_range_max)
|
44
|
-
|
46
|
+
end
|
45
47
|
candidate
|
46
|
-
|
48
|
+
end
|
47
49
|
end
|
48
50
|
|
49
51
|
# Determines the fitness of the population and thereafter sorts it
|
50
52
|
# based on fitness descdending (high fitness first, low fitness last).
|
51
53
|
def train
|
52
54
|
average_fitness = 0
|
53
|
-
@candidates.each
|
55
|
+
@candidates.each do |c|
|
54
56
|
c.fitness = fitness_function.call(c.dna)
|
55
|
-
average_fitness
|
56
|
-
|
57
|
-
|
57
|
+
average_fitness += + c.fitness
|
58
|
+
end
|
59
|
+
|
60
|
+
average_fitness /= @population_size
|
58
61
|
|
59
62
|
@average_fitness << average_fitness
|
60
63
|
|
61
|
-
@candidates = @candidates.sort_by
|
64
|
+
@candidates = @candidates.sort_by(&:fitness)
|
62
65
|
@candidates = @candidates.reverse
|
63
66
|
end
|
64
67
|
|
65
68
|
# Performs simple mechanism of crossover - in this case picks two
|
66
|
-
# random candidates in from a top percentile of the population and
|
69
|
+
# random candidates in from a top percentile of the population and
|
67
70
|
# performs one point crossover, producing new offspring equal to the
|
68
71
|
# population size attribute.
|
69
72
|
def crossover
|
70
73
|
# Define the candidates that can have children.
|
71
|
-
@candidates = @candidates.take((@population_size*0.75).to_i)
|
72
|
-
|
73
|
-
new_generation =
|
74
|
-
|
75
|
-
(0...@population_size).each
|
74
|
+
@candidates = @candidates.take((@population_size * 0.75).to_i)
|
75
|
+
|
76
|
+
new_generation = []
|
77
|
+
|
78
|
+
(0...@population_size).each do
|
76
79
|
# For each of the top 75% of the population take 2
|
77
80
|
couple = @candidates.sample(2)
|
78
81
|
params = @crossover_params
|
79
82
|
|
80
83
|
children = @crossover_function.call(couple, params)
|
81
84
|
|
82
|
-
new_generation
|
83
|
-
|
85
|
+
new_generation += children
|
86
|
+
|
84
87
|
# When we go above set population_size, take the first population_size
|
85
88
|
# candidates, ignore the rest.
|
86
|
-
if new_generation.length >=
|
87
|
-
new_generation = new_generation.take(
|
89
|
+
if new_generation.length >= population_size
|
90
|
+
new_generation = new_generation.take(population_size)
|
88
91
|
break
|
89
92
|
end
|
90
|
-
|
93
|
+
end
|
94
|
+
|
91
95
|
@candidates = new_generation
|
92
96
|
end
|
93
97
|
|
94
|
-
# Performs simple mutation over the next generation. In this case,
|
95
|
-
# it either adds or substracts an amount to each dimension given the
|
98
|
+
# Performs simple mutation over the next generation. In this case,
|
99
|
+
# it either adds or substracts an amount to each dimension given the
|
96
100
|
# mutation range attributes.
|
97
101
|
def mutate
|
98
|
-
mutated = @candidates.sample(
|
102
|
+
mutated = @candidates.sample(mutation_num)
|
99
103
|
|
100
|
-
mutated.each
|
101
|
-
(0...@dna_len).each
|
104
|
+
mutated.each do |c|
|
105
|
+
(0...@dna_len).each do |i|
|
102
106
|
c.dna[i] = c.dna[i] + Random.rand(@mutation_range_min...@mutation_range_max)
|
103
|
-
|
104
|
-
|
107
|
+
end
|
108
|
+
end
|
105
109
|
end
|
106
110
|
end
|
data/lib/evopop.rb
CHANGED
@@ -2,13 +2,6 @@ require 'evopop/population'
|
|
2
2
|
require 'evopop/candidate'
|
3
3
|
require 'evopop/crossover'
|
4
4
|
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
Population
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.Candidate
|
12
|
-
Candidate
|
13
|
-
end
|
14
|
-
end
|
5
|
+
# Toplevel class for evopop project
|
6
|
+
module Evopop
|
7
|
+
end
|
metadata
CHANGED
@@ -1,27 +1,28 @@
|
|
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.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elvin Lucero
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A simple library for implementing simple genetic algorithms.
|
14
|
-
email:
|
14
|
+
email: elvin+evopop@pluots.io
|
15
15
|
executables: []
|
16
16
|
extensions: []
|
17
17
|
extra_rdoc_files: []
|
18
18
|
files:
|
19
|
+
- LICENSE
|
20
|
+
- README.md
|
19
21
|
- lib/evopop.rb
|
20
|
-
- lib/evopop/population.rb
|
21
22
|
- lib/evopop/candidate.rb
|
22
23
|
- lib/evopop/crossover.rb
|
23
|
-
-
|
24
|
-
homepage: https://
|
24
|
+
- lib/evopop/population.rb
|
25
|
+
homepage: https://rubygems.org/gems/evopop
|
25
26
|
licenses:
|
26
27
|
- MIT
|
27
28
|
metadata: {}
|
@@ -31,19 +32,18 @@ require_paths:
|
|
31
32
|
- lib
|
32
33
|
required_ruby_version: !ruby/object:Gem::Requirement
|
33
34
|
requirements:
|
34
|
-
- -
|
35
|
+
- - ">="
|
35
36
|
- !ruby/object:Gem::Version
|
36
37
|
version: '0'
|
37
38
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
39
|
requirements:
|
39
|
-
- -
|
40
|
+
- - ">="
|
40
41
|
- !ruby/object:Gem::Version
|
41
42
|
version: '0'
|
42
43
|
requirements: []
|
43
44
|
rubyforge_project:
|
44
|
-
rubygems_version: 2.
|
45
|
+
rubygems_version: 2.4.5.1
|
45
46
|
signing_key:
|
46
47
|
specification_version: 4
|
47
|
-
summary:
|
48
|
-
test_files:
|
49
|
-
- test/test_population.rb
|
48
|
+
summary: A simple library for implementing simple genetic algorithms.
|
49
|
+
test_files: []
|
data/test/test_population.rb
DELETED
@@ -1,120 +0,0 @@
|
|
1
|
-
require 'test/unit'
|
2
|
-
require 'evopop'
|
3
|
-
|
4
|
-
class PopulationTest < Test::Unit::TestCase
|
5
|
-
attr_accessor :population
|
6
|
-
|
7
|
-
def initialize_population
|
8
|
-
population = Population.new
|
9
|
-
population.population_size = 100
|
10
|
-
population.dna_len = 2
|
11
|
-
population.max_generations = 10000
|
12
|
-
population.initial_range_min = -10000.0
|
13
|
-
population.initial_range_max = 10000.0
|
14
|
-
population.mutation_range_min = -100.0
|
15
|
-
population.mutation_range_max = 100.0
|
16
|
-
population.mutation_num = 10
|
17
|
-
population.crossover_params = {:ordinal => (population.dna_len/2)}
|
18
|
-
population.crossover_function = Crossover.method(:one_point)
|
19
|
-
population.fitness_function = Proc.new { |dna|
|
20
|
-
Math.sin(dna[0]) + Math.cos(dna[1])
|
21
|
-
}
|
22
|
-
population.create
|
23
|
-
population
|
24
|
-
end
|
25
|
-
|
26
|
-
# Simple test to assure functions in the Population file are properly
|
27
|
-
# initializing the population parameters.
|
28
|
-
def test_initialize_population
|
29
|
-
# Arrange and Act: Initialize the population
|
30
|
-
population = initialize_population
|
31
|
-
|
32
|
-
# Assert: Check that the given properties are initialized correctly.
|
33
|
-
assert_equal(population.candidates.length, population.population_size)
|
34
|
-
assert_equal(true, population.fitness_function.is_a?(Proc))
|
35
|
-
|
36
|
-
population.candidates.each { |c|
|
37
|
-
assert_equal(c.dna.length, population.dna_len)
|
38
|
-
assert_equal(true, c.dna[0] > population.initial_range_min)
|
39
|
-
assert_equal(true, c.dna[1] < population.initial_range_max)
|
40
|
-
}
|
41
|
-
end
|
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.
|
47
|
-
def test_train
|
48
|
-
# Arrange: Initialize the population
|
49
|
-
population = initialize_population
|
50
|
-
|
51
|
-
# Act: Train the population based on default fitness function
|
52
|
-
population.train
|
53
|
-
|
54
|
-
# Assert: Training has sorted the population by fitness properly
|
55
|
-
population.candidates.length.times { |count|
|
56
|
-
assert_equal(population.candidates[count].fitness.nil?, false)
|
57
|
-
|
58
|
-
if count > 0
|
59
|
-
assert_equal(true, population.candidates[count].fitness <= population.candidates[count-1].fitness)
|
60
|
-
end
|
61
|
-
}
|
62
|
-
end
|
63
|
-
|
64
|
-
# Simple test to ensure that only the exact number of candidates in the
|
65
|
-
# population are mutated.
|
66
|
-
def test_mutation
|
67
|
-
# Arrange: Initialize the population
|
68
|
-
population = initialize_population
|
69
|
-
old_candidates = Marshal.load(Marshal.dump(population.candidates))
|
70
|
-
|
71
|
-
# Act: Train the population based on default fitness function
|
72
|
-
population.mutate
|
73
|
-
|
74
|
-
# Assert: Only the specified number of candidates are being mutated
|
75
|
-
counter = 0
|
76
|
-
old_candidates.zip(population.candidates).each {|old_candidate, new_candidate|
|
77
|
-
if old_candidate.dna.to_s != new_candidate.dna.to_s
|
78
|
-
assert_equal(true, (old_candidate.dna[0] - new_candidate.dna[0]).abs <= population.mutation_range_max)
|
79
|
-
assert_equal(true, (old_candidate.dna[1] - new_candidate.dna[1]).abs <= population.mutation_range_max)
|
80
|
-
counter = counter + 1
|
81
|
-
end
|
82
|
-
}
|
83
|
-
|
84
|
-
assert_equal(population.mutation_num, counter)
|
85
|
-
end
|
86
|
-
|
87
|
-
# Simple
|
88
|
-
def test_one_point_crossover
|
89
|
-
# Arrange: Initialize the population
|
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
|
-
}
|
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])
|
102
|
-
end
|
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
|
120
|
-
end
|