charlie 0.5.0
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.
- data/History.txt +3 -0
- data/Manifest.txt +53 -0
- data/README.txt +90 -0
- data/Rakefile +43 -0
- data/TODO.txt +28 -0
- data/data/BENCHMARK +53 -0
- data/data/CROSSOVER +49 -0
- data/data/GENOTYPE +49 -0
- data/data/MUTATION +43 -0
- data/data/SELECTION +48 -0
- data/data/template.html +34 -0
- data/examples/bit.rb +10 -0
- data/examples/function_opt_2peak.rb +24 -0
- data/examples/function_opt_sombero.rb +38 -0
- data/examples/gladiatorial_simple.rb +17 -0
- data/examples/gladiatorial_sunburn.rb +89 -0
- data/examples/gridwalk.rb +29 -0
- data/examples/output/flattened_sombero.html +6400 -0
- data/examples/output/flattened_sombero2_.html +3576 -0
- data/examples/output/fopt1_dblopt.html +2160 -0
- data/examples/output/hill10.html +5816 -0
- data/examples/output/hill2.csv +24 -0
- data/examples/output/hill2.html +384 -0
- data/examples/output/royalroad1_report.html +1076 -0
- data/examples/output/royalroad2_report.html +1076 -0
- data/examples/output/royalroadquick_report.html +504 -0
- data/examples/output/tsp.html +632 -0
- data/examples/output/weasel1_report.html +1076 -0
- data/examples/output/weasel2_report.html +240 -0
- data/examples/royalroad.rb +26 -0
- data/examples/royalroad2.rb +18 -0
- data/examples/simple_climb_hill2.rb +47 -0
- data/examples/tsp.rb +35 -0
- data/examples/weasel.rb +36 -0
- data/lib/charlie.rb +35 -0
- data/lib/charlie/crossover.rb +49 -0
- data/lib/charlie/etc/minireport.rb +45 -0
- data/lib/charlie/etc/monkey.rb +136 -0
- data/lib/charlie/genotype.rb +45 -0
- data/lib/charlie/list/list_crossover.rb +30 -0
- data/lib/charlie/list/list_genotype.rb +53 -0
- data/lib/charlie/list/list_mutate.rb +75 -0
- data/lib/charlie/mutate.rb +25 -0
- data/lib/charlie/permutation/permutation.rb +47 -0
- data/lib/charlie/population.rb +156 -0
- data/lib/charlie/selection.rb +162 -0
- data/test/t_common.rb +32 -0
- data/test/test_basic.rb +32 -0
- data/test/test_benchmark.rb +56 -0
- data/test/test_cross.rb +28 -0
- data/test/test_mutator.rb +44 -0
- data/test/test_permutation.rb +23 -0
- data/test/test_sel.rb +39 -0
- metadata +115 -0
@@ -0,0 +1,156 @@
|
|
1
|
+
# Contains the Population class
|
2
|
+
|
3
|
+
|
4
|
+
# The population class represents an array of genotypes.
|
5
|
+
# Create an instance of this, and call one of the evolve functions to run the genetic algorithm.
|
6
|
+
class Population
|
7
|
+
attr_reader :size, :population
|
8
|
+
def initialize(genotype_class,population_size=20)
|
9
|
+
@size = population_size
|
10
|
+
@genotype_class = genotype_class
|
11
|
+
@population = Array.new(population_size){ genotype_class.new }
|
12
|
+
end
|
13
|
+
|
14
|
+
# Runs the genetic algorithm with some stats on the console. Returns the population sorted by fitness.
|
15
|
+
def evolve_on_console(generations=100)
|
16
|
+
puts "Generation\tBest Fitness\t\tBest Individual"
|
17
|
+
generations.times{|g|
|
18
|
+
best = @population.max
|
19
|
+
puts "#{g}\t\t#{best.fitness}\t\t#{best.to_s}"
|
20
|
+
@population = @genotype_class.next_generation(@population){|*parents|
|
21
|
+
ch = [*@genotype_class.cross(*parents)]
|
22
|
+
ch.each{|c| c.mutate! }
|
23
|
+
ch
|
24
|
+
}
|
25
|
+
}
|
26
|
+
@population = @population.sort_by{|x|x.fitness}
|
27
|
+
puts "Finished: Best fitness = #{@population[-1].fitness}"
|
28
|
+
@population
|
29
|
+
end
|
30
|
+
|
31
|
+
# Runs the genetic algorithm without any output. Returns the population sorted by fitness (unsorted for co-evolution).
|
32
|
+
def evolve_silent(generations=100)
|
33
|
+
generations.times{|g|
|
34
|
+
@population = @genotype_class.next_generation(@population){|*parents|
|
35
|
+
ch = [*@genotype_class.cross(*parents)]
|
36
|
+
ch.each{|c| c.mutate! }
|
37
|
+
ch
|
38
|
+
}
|
39
|
+
}
|
40
|
+
@population.sort_by{|x|x.fitness} rescue @population
|
41
|
+
end
|
42
|
+
|
43
|
+
alias :evolve :evolve_on_console
|
44
|
+
|
45
|
+
class << self
|
46
|
+
|
47
|
+
# This method generates reports comparing several selection/crossover/mutation methods. Check the examples directory for several examples. See the BENCHMARK documentation file for more information.
|
48
|
+
def benchmark(genotype_class, html_outfile='report.html', csv_outfile=nil, &b)
|
49
|
+
start = Time.now
|
50
|
+
|
51
|
+
d = StrategiesDSL.new; d.instance_eval(&b)
|
52
|
+
tries = d.get_tests
|
53
|
+
gens = d.generations
|
54
|
+
pop_size = d.population_size
|
55
|
+
rep = d.repeat
|
56
|
+
puts "#{tries.size} Total tests:"; done=0
|
57
|
+
|
58
|
+
data = tries.map{|s,c,m|
|
59
|
+
print "\nRunning test #{done+=1}/#{tries.size} : #{s} / #{c} / #{m}\t"
|
60
|
+
gclass = Class.new(genotype_class) { use s,c,m }
|
61
|
+
start_try = Time.now
|
62
|
+
bestf = (0...rep).map{
|
63
|
+
print '.'; $stdout.flush
|
64
|
+
Population.new(gclass,pop_size).evolve_silent(gens)[-1].fitness # todo: generations
|
65
|
+
}
|
66
|
+
[s,c,m, (Time.now-start_try) / rep, bestf]
|
67
|
+
}
|
68
|
+
|
69
|
+
# Generate HTML
|
70
|
+
html_tables = <<INFO
|
71
|
+
<h1>Information</h1>\n
|
72
|
+
<table>
|
73
|
+
<tr><td>Genotype class</td><td>#{genotype_class}</td></tr>
|
74
|
+
<tr><td>Population size</td><td>#{pop_size}</td></tr>
|
75
|
+
<tr><td># of generations per run</td><td>#{gens}</td></tr>
|
76
|
+
<tr><td>Number of tests </td><td>#{tries.size}</td></tr>
|
77
|
+
<tr><td>Tests repeated </td><td>#{rep} times</td></tr>
|
78
|
+
<tr><td>Number of runs </td><td>#{tries.size*rep}</td></tr>
|
79
|
+
<tr><td>Total number of generations </td><td>#{tries.size*rep*gens}</td></tr>
|
80
|
+
<tr><td>Total time</td><td>#{'%.2f' % (Time.now-start)} seconds</td></tr>
|
81
|
+
</table
|
82
|
+
INFO
|
83
|
+
|
84
|
+
# - combined stats
|
85
|
+
joined = data.map(&:last).flatten
|
86
|
+
avg_time = data.map{|r| r[-2] }.average
|
87
|
+
|
88
|
+
html_tables << "<h1>Stats for all</h1>"
|
89
|
+
html_tables << [["All"] + (joined.stats << avg_time).map{|f| '%.5f' % f } ].to_table(%w[. min max avg stddev avg-time]).to_html
|
90
|
+
# - stats grouped by selection, crossover, mutation methods
|
91
|
+
["selection","crossover","mutation"].each_with_index{|title,i|
|
92
|
+
data_grp = data.group_by{|x|x[i]}.map{|k,a|
|
93
|
+
joined = a.map(&:last).flatten
|
94
|
+
avg_time = a.map{|r| r[-2] }.average
|
95
|
+
[k] + (joined.stats << avg_time).map{|f| '%.5f' % f }
|
96
|
+
}.sort_by{|row| -row[-3].to_f } # sort by average
|
97
|
+
html_tables << "<h1>Stats for #{title}</h1>"
|
98
|
+
html_tables << data_grp.to_table([title]+%w[min max avg stddev avg-time]).to_html
|
99
|
+
}
|
100
|
+
# - detailed stats
|
101
|
+
|
102
|
+
tbl = data.map{|s,c,m,t,a| [s,c,m] + (a.stats << t).map{|f| '%.5f' % f } }.sort_by{|row| -row[-3].to_f }.to_table(%w[selection crossover mutation min max avg stddev avg-time])
|
103
|
+
html_tables << '<h1>Raw Stats</h1>' << tbl.to_html
|
104
|
+
# write HTML stats
|
105
|
+
File.open(html_outfile,'w'){|f| f << File.read(File.dirname(__FILE__)+"/../../data/template.html").sub('{{CONTENT}}',html_tables) } if html_outfile
|
106
|
+
|
107
|
+
# write csv file, contains raw stats
|
108
|
+
File.open(csv_outfile,'w'){|f| f << data.map{|r|r[0..-2] << r[-1].join(', ') }.to_table.to_csv } if csv_outfile
|
109
|
+
|
110
|
+
puts '',tbl.to_s
|
111
|
+
data
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
# Used in the Population#benchmark function.
|
119
|
+
class StrategiesDSL
|
120
|
+
# Number of generations run in each test.
|
121
|
+
attr_accessor :generations
|
122
|
+
# Population size used.
|
123
|
+
attr_accessor :population_size
|
124
|
+
# Number of times all tests are run. Default=10. Increase for more accuracy on the benchmark.
|
125
|
+
attr_accessor:repeat
|
126
|
+
|
127
|
+
def initialize
|
128
|
+
@repeat = 10
|
129
|
+
@population_size = 20
|
130
|
+
@generations = 50
|
131
|
+
end
|
132
|
+
|
133
|
+
# Pass several modules to this to test these selection methods.
|
134
|
+
def selection(*s); @s=s; end
|
135
|
+
# Pass several modules to this to test these crossover methods.
|
136
|
+
def crossover(*c); @c=c; end
|
137
|
+
# Pass several modules to this to test these mutation methods.
|
138
|
+
def mutator(*m) ; @m=m; end
|
139
|
+
alias :mutation :mutator
|
140
|
+
# Get all the tests. Basically a cartesian product of all selection, crossover and mutation methods.
|
141
|
+
def get_tests
|
142
|
+
t = []
|
143
|
+
raise 'No selection modules defined' unless @s
|
144
|
+
raise 'No crossover modules defined' unless @c
|
145
|
+
raise 'No mutation modules defined' unless @m
|
146
|
+
@s.each{|s|
|
147
|
+
@c.each{|c|
|
148
|
+
@m.each{|m|
|
149
|
+
t << [s,c,m]
|
150
|
+
}
|
151
|
+
}
|
152
|
+
}
|
153
|
+
t
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# Contains the selection methods.
|
2
|
+
|
3
|
+
|
4
|
+
# Random selection. May be useful in benchmarks, but otherwise useless.
|
5
|
+
module RandomSelection
|
6
|
+
def next_generation(population)
|
7
|
+
new_pop = []
|
8
|
+
new_pop += yield(population.at_rand,population.at_rand) while new_pop.size < population.size
|
9
|
+
new_pop.pop until new_pop.size == population.size
|
10
|
+
new_pop
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Truncation selection takes the +best+ invididuals with the highest fitness
|
15
|
+
# and crosses them randomly to replace all the others.
|
16
|
+
# +best+ can be an float < 1 (fraction of population size), or a number >=1 (number of individuals)
|
17
|
+
def TruncationSelection(best=0.3)
|
18
|
+
Module::new{
|
19
|
+
@@best = best
|
20
|
+
def next_generation(population)
|
21
|
+
k = if @@best >= 1 # best is an integer >= 1 -> select this much
|
22
|
+
@@best.round
|
23
|
+
else # best is a float < 1 -> select this percentage
|
24
|
+
[1,(@@best * population.size).round].max
|
25
|
+
end
|
26
|
+
n_new = population.size - k
|
27
|
+
|
28
|
+
population = population.sort_by(&:fitness)
|
29
|
+
best = population[-k..-1]
|
30
|
+
new_pop = []
|
31
|
+
new_pop += yield(best.at_rand,best.at_rand) while new_pop.size < n_new
|
32
|
+
new_pop.pop until new_pop.size == n_new
|
33
|
+
|
34
|
+
new_pop + best
|
35
|
+
end
|
36
|
+
self.name = "TruncationSelection(#{best})"
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
# This selection algorithm is basically randomized hill climbing.
|
41
|
+
BestOnlySelection = TruncationSelection(1)
|
42
|
+
|
43
|
+
# Roulette selection without replacement. Probability of individual i being selected is fitness(i) / sum fitness(1..population size)
|
44
|
+
module RouletteSelection
|
45
|
+
def next_generation(population)
|
46
|
+
partial_sum = []
|
47
|
+
sum = population.inject(0){|a,b| cs = a + b.fitness; partial_sum << cs; cs }
|
48
|
+
|
49
|
+
new_pop = []
|
50
|
+
while new_pop.size < population.size
|
51
|
+
i1,i2 = [0,0].map{
|
52
|
+
r = rand * sum
|
53
|
+
partial_sum.index partial_sum.find{|x| x > r }
|
54
|
+
} until i1!=i2 # no replacement, except when this fails
|
55
|
+
new_pop += yield(population[i1],population[i2])
|
56
|
+
end
|
57
|
+
new_pop.pop until new_pop.size == population.size
|
58
|
+
new_pop
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
# Scaled Roulette selection without replacement.
|
64
|
+
# Sorts by fitness, scales fitness by the values the given block yields for its index, then applies Roulette selection to the resulting fitness values.
|
65
|
+
# Default is Rank selection: {|x| x+1 }
|
66
|
+
def ScaledRouletteSelection(&block)
|
67
|
+
Module.new{
|
68
|
+
block = proc{|x|x+1} if block.nil?
|
69
|
+
@@block = block
|
70
|
+
@@index = nil
|
71
|
+
@@index_size = 0 # population size used to generate index
|
72
|
+
|
73
|
+
def next_generation(population)
|
74
|
+
|
75
|
+
if @@index_size != population.size # build index, cache for constant population size
|
76
|
+
@@index_size = population.size
|
77
|
+
@@index = []
|
78
|
+
(0...population.size).map(&@@block).each_with_index{|e,i| @@index += Array.new(e.round,i) }
|
79
|
+
end
|
80
|
+
|
81
|
+
population = population.sort_by(&:fitness)
|
82
|
+
new_pop = []
|
83
|
+
while new_pop.size < population.size
|
84
|
+
i1,i2 = @@index.at_rand, @@index.at_rand until i1!=i2 # no replacement
|
85
|
+
new_pop += yield(population[i1],population[i2])
|
86
|
+
end
|
87
|
+
new_pop.pop until new_pop.size == population.size
|
88
|
+
new_pop
|
89
|
+
end
|
90
|
+
|
91
|
+
self.name= "ScaledRouletteSelection[#{(0..3).map(&block).map(&:to_s).join(',')},...]"
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
ScaledRouletteSelection = ScaledRouletteSelection()
|
96
|
+
|
97
|
+
# Generates a selection module with elitism from a normal selection module.
|
98
|
+
# Elitism is saving the best +elite_n+ individuals each generation, to ensure the best solutions are never lost.
|
99
|
+
def Elitism(sel_module,elite_n=1)
|
100
|
+
Module.new{
|
101
|
+
include sel_module
|
102
|
+
@@elite_n = elite_n
|
103
|
+
def next_generation(population)
|
104
|
+
population = population.sort_by(&:fitness)
|
105
|
+
best = population[-@@elite_n..-1]
|
106
|
+
population = super
|
107
|
+
# reset old best elite_n, but don't overwrite better ones
|
108
|
+
population[-@@elite_n..-1] = best.zip_with(population[-@@elite_n..-1]){|old,new| [old,new].max }
|
109
|
+
population
|
110
|
+
end
|
111
|
+
self.name= "Elitism(#{sel_module.to_s},#{elite_n})"
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
# Tournament selection.
|
118
|
+
# Default: select the 2 individuals with the highest fitness out of a random population with size group_size
|
119
|
+
# and replaces the others with offspring of these 2.
|
120
|
+
# Does this n_times. n_times==nil takes population size / (group_size-2) , i.e. about the same number of new individuals as roulette selection etc.
|
121
|
+
def TournamentSelection(group_size=4,n_times=nil) # TODO: maybe expand for probabilistic selection(?) Or in a new module
|
122
|
+
Module::new{
|
123
|
+
@@group_size = group_size
|
124
|
+
@@n_times = n_times
|
125
|
+
def next_generation(population)
|
126
|
+
@@n_times ||= population.size / (@@group_size-2)
|
127
|
+
@@n_times.times{
|
128
|
+
ix=[]
|
129
|
+
begin
|
130
|
+
ix = (0...@@group_size).map{ population.rand_index }
|
131
|
+
end while ix.uniq.size != @@group_size
|
132
|
+
ix=ix.sort_by{|i| population[i].fitness }
|
133
|
+
p1,p2 = population[ix[-1]],population[ix[-2]]
|
134
|
+
nw = [];
|
135
|
+
nw += yield(p1,p2) while nw.size < @@group_size-2
|
136
|
+
(@@group_size-2).times{|i| population[ix[i]] = nw[i] }
|
137
|
+
}
|
138
|
+
population
|
139
|
+
end
|
140
|
+
self.name= "TournamentSelection(#{group_size},#{n_times.inspect})"
|
141
|
+
}
|
142
|
+
end
|
143
|
+
TournamentSelection = TournamentSelection()
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
# Direct competition (gladiatorial) selection
|
148
|
+
module GladiatorialSelection
|
149
|
+
def next_generation(population)
|
150
|
+
(population.size/2).times{
|
151
|
+
ix=[0,0,0,0]
|
152
|
+
ix=(0..3).map{population.rand_index} while ix.uniq.size < 4
|
153
|
+
ix[0],ix[1] = ix[1],ix[0] unless population[ix[0]].fight(population[ix[1]]) # 0 and 2 hold winners
|
154
|
+
ix[2],ix[3] = ix[3],ix[2] unless population[ix[2]].fight(population[ix[3]])
|
155
|
+
nw = [];
|
156
|
+
nw += yield(population[ix[0]],population[ix[2]]) while nw.size < 2
|
157
|
+
population[ix[1]],population[ix[3]] = *nw
|
158
|
+
}
|
159
|
+
population
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
data/test/t_common.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
require File.dirname(__FILE__) + '/../lib/charlie' unless Object.const_defined?('Charlie')
|
6
|
+
require 'test/unit'
|
7
|
+
|
8
|
+
$crs_mth = [NullCrossover, SinglePointCrossover, UniformCrossover]
|
9
|
+
|
10
|
+
|
11
|
+
class TestProblem < FloatListGenotype(2,0..1)
|
12
|
+
def fitness
|
13
|
+
genes.map{|x| [x,10].min }.sum
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def TestClass(mod,klass=TestProblem)
|
18
|
+
Class.new(klass){use mod}
|
19
|
+
end
|
20
|
+
|
21
|
+
class RoyalRoad < BitStringGenotype(64)
|
22
|
+
def fitness
|
23
|
+
genes.enum_slice(8).find_all{|e| e.all?{|x|x==1} }.size
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class StringA < StringGenotype(20,'a'..'d')
|
28
|
+
def fitness
|
29
|
+
genes.find_all{|c| c=='a'}.size
|
30
|
+
end
|
31
|
+
use UniformCrossover
|
32
|
+
end
|
data/test/test_basic.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 't_common'
|
2
|
+
|
3
|
+
class BasicTest < Test::Unit::TestCase
|
4
|
+
def test_console
|
5
|
+
r = Population.new(TestProblem,10).evolve_on_console(10)
|
6
|
+
assert_respond_to r, :[]
|
7
|
+
assert_respond_to r[-1], :fitness
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_silent
|
11
|
+
r = Population.new(TestProblem,10).evolve_silent(10)
|
12
|
+
assert_respond_to r, :[]
|
13
|
+
assert_respond_to r[-1], :fitness
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_continue
|
17
|
+
p = Population.new(TestProblem,10)
|
18
|
+
best3 = p.evolve_silent(3)[-1].fitness
|
19
|
+
best33 = p.evolve_silent(30)[-1].fitness
|
20
|
+
# Not true for all problems, but in this case the probability of failure is negligible
|
21
|
+
assert( best33 > best3, "test_continue failed. Please rerun test to make sure this isn't just extremely bad luck.")
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_testclass
|
25
|
+
assert_nothing_raised{
|
26
|
+
Population.new(TestClass(Module::new),10).evolve_silent(10)
|
27
|
+
Population.new(TestClass(UniformCrossover),10).evolve_silent(10)
|
28
|
+
Population.new(TestClass(ListMutator(:single_point,:gaussian) ),10).evolve_silent(10)
|
29
|
+
Population.new(TestClass(TournamentSelection(4)),10).evolve_silent(10)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 't_common'
|
2
|
+
|
3
|
+
CDIR = File.dirname(__FILE__)
|
4
|
+
|
5
|
+
class BMTest < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
Dir[CDIR+'/output/*'].each{|f| File.unlink f}
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_basic_bm
|
11
|
+
assert_nothing_raised {
|
12
|
+
Population.benchmark(RoyalRoad,CDIR+'/output/test_benchmark.html'){
|
13
|
+
selection TruncationSelection(0.2), Elitism(ScaledRouletteSelection(),1)
|
14
|
+
crossover NullCrossover, UniformCrossover
|
15
|
+
mutator ListMutator(:single_point,:flip), ListMutator(:expected_n[4],:flip)
|
16
|
+
self.repeat = 5
|
17
|
+
self.generations = 40
|
18
|
+
self.population_size = 10
|
19
|
+
}
|
20
|
+
}
|
21
|
+
assert( File.read(CDIR+'/output/test_benchmark.html').size > 1000, 'Output file not generated.')
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def test_csv
|
26
|
+
assert_nothing_raised {
|
27
|
+
Population.benchmark(StringA,nil,CDIR+'/output/test_benchmark.csv'){
|
28
|
+
selection TruncationSelection(0.2)
|
29
|
+
crossover NullCrossover, Module.new{self.name='default'}
|
30
|
+
mutation ListMutator(:expected_n[1],:replace[*'a'..'d']), NullMutator
|
31
|
+
}
|
32
|
+
}
|
33
|
+
assert( File.readlines(CDIR+'/output/test_benchmark.csv').size == 4, 'Output file not generated.')
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_raise
|
37
|
+
assert_raises(RuntimeError) {
|
38
|
+
Population.benchmark(StringA,nil,nil){
|
39
|
+
selection TruncationSelection(0.2)
|
40
|
+
# no crossover, etc specified.
|
41
|
+
}
|
42
|
+
}
|
43
|
+
assert_raises(RuntimeError) {
|
44
|
+
Population.benchmark(StringA,nil,nil){
|
45
|
+
crossover NullCrossover
|
46
|
+
}
|
47
|
+
}
|
48
|
+
assert_raises(RuntimeError) {
|
49
|
+
Population.benchmark(StringA,nil,nil){
|
50
|
+
mutation Module.new
|
51
|
+
selection Module.new
|
52
|
+
}
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
data/test/test_cross.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 't_common'
|
2
|
+
|
3
|
+
class TestCrossover < Test::Unit::TestCase
|
4
|
+
def test_cross
|
5
|
+
$crs_mth.each{|s|
|
6
|
+
klass = TestClass(s)
|
7
|
+
assert_nothing_raised{ Population.new(klass,10).evolve_silent(10) }
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_singlechild
|
12
|
+
$crs_mth.map{|c| SingleChild(c) }.each{|s|
|
13
|
+
klass = TestClass(s)
|
14
|
+
assert_nothing_raised{ Population.new(klass,10).evolve_silent(10) }
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_pcross
|
19
|
+
$crs_mth.map{|c| PCross(0.5,c) }.each{|s|
|
20
|
+
klass = TestClass(s)
|
21
|
+
assert_nothing_raised{ Population.new(klass,10).evolve_silent(10) }
|
22
|
+
}
|
23
|
+
$crs_mth.map{|c| PCross(0.5,c,$crs_mth[-2]) }.each{|s|
|
24
|
+
klass = TestClass(s)
|
25
|
+
assert_nothing_raised{ Population.new(klass,10).evolve_silent(10) }
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|