charlie 0.8.0 → 0.8.1
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 +6 -0
- data/Manifest.txt +1 -0
- data/README.txt +1 -1
- data/TODO.txt +3 -3
- data/data/CROSSOVER +2 -0
- data/examples/output/bitstring_royalroad.html +1243 -0
- data/examples/output/function_optimization_sombrero.html +1486 -1486
- data/examples/output/function_optimization_twopeak.csv +210 -210
- data/examples/output/function_optimization_twopeak.html +1608 -1608
- data/examples/output/string_weasel.html +235 -235
- data/examples/output/tsp.html +681 -523
- data/examples/tsp.rb +1 -1
- data/lib/charlie.rb +1 -1
- data/lib/charlie/1.9fixes.rb +2 -2
- data/lib/charlie/etc/monkey.rb +9 -2
- data/lib/charlie/list/list_crossover.rb +1 -1
- data/lib/charlie/permutation/permutation.rb +37 -0
- data/lib/charlie/population.rb +3 -1
- data/lib/charlie/selection.rb +11 -9
- data/test/test_permutation.rb +13 -1
- metadata +6 -4
data/examples/tsp.rb
CHANGED
@@ -40,7 +40,7 @@ GABenchmark.benchmark(TSP,'output/tsp.html') {
|
|
40
40
|
selection TruncationSelection(1), Elitism(ScaledRouletteSelection), TournamentSelection(4)
|
41
41
|
crossover EdgeRecombinationCrossover, PermutationCrossover,
|
42
42
|
PCross(0.5,EdgeRecombinationCrossover,PermutationCrossover),
|
43
|
-
PCross(0.01,EdgeRecombinationCrossover), NullCrossover
|
43
|
+
PCross(0.01,EdgeRecombinationCrossover), NullCrossover, PartiallyMappedCrossover
|
44
44
|
|
45
45
|
mutator TranspositionMutator, InversionMutator, InsertionMutator,
|
46
46
|
PMutateN(InversionMutator=>0.4,InsertionMutator=>0.4),
|
data/lib/charlie.rb
CHANGED
data/lib/charlie/1.9fixes.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#1.9 versions of some functions, to avoid bug #16493.
|
2
2
|
#TODO: remove on bugfix/1.9.1
|
3
|
-
|
3
|
+
#Bug fixed by now, really should update
|
4
4
|
|
5
5
|
|
6
6
|
def Elitism(sel_module,elite_n=1) # :nodoc:
|
@@ -43,4 +43,4 @@ module Enumerable
|
|
43
43
|
def zip_with(a2) # avoid Enumerable#zip in 1.9
|
44
44
|
r=[]; each_with_index{|e,i| r << yield(e,a2[i]) }; r
|
45
45
|
end
|
46
|
-
end
|
46
|
+
end
|
data/lib/charlie/etc/monkey.rb
CHANGED
@@ -45,11 +45,11 @@ class Array
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def rand_index
|
48
|
-
rand(size)
|
48
|
+
Kernel.rand(size)
|
49
49
|
end
|
50
50
|
|
51
51
|
def at_rand
|
52
|
-
self[rand(size)]
|
52
|
+
self[Kernel.rand(size)]
|
53
53
|
end
|
54
54
|
|
55
55
|
def stats # TODO 1.9, use minmax
|
@@ -71,6 +71,13 @@ class Array
|
|
71
71
|
each_with_index{|e,i| r << yield(e,i) }
|
72
72
|
r
|
73
73
|
end
|
74
|
+
|
75
|
+
# Finds value and swaps it with the element at index.
|
76
|
+
def swap_element_at_index!(index, value)
|
77
|
+
old_element_index = self.index(value)
|
78
|
+
self[index], self[old_element_index] = value, self[index]
|
79
|
+
end
|
80
|
+
|
74
81
|
end
|
75
82
|
|
76
83
|
|
@@ -124,3 +124,40 @@ module EdgeRecombinationCrossover
|
|
124
124
|
end
|
125
125
|
end
|
126
126
|
|
127
|
+
# Two point Partial Preservation Crossover for PermutationGenotype, also known as Partially Mapped Crossover (PMX).
|
128
|
+
#
|
129
|
+
# The PMX proceeds by choosing two cut points at random:
|
130
|
+
# Parent 1: hkcefd bla igj
|
131
|
+
# Parent 2: abcdef ghi jkl
|
132
|
+
#
|
133
|
+
# The cut-out section defines a series of swapping operations to be performed on the second parent.
|
134
|
+
# In the example case, we swap b with g, l with h and a with i and end up with the following offspring:
|
135
|
+
# Offspring: igcdef bla jkh
|
136
|
+
# Performing similar swapping on the first parent gives the other offspring:
|
137
|
+
# Offspring: lkcefd ghi abj
|
138
|
+
#
|
139
|
+
# Algortithm and description taken from:
|
140
|
+
#
|
141
|
+
# "A New Genetic Algorithm For VPRTW", Kenny Qili Zhu, National University of Singapure,
|
142
|
+
# April 13, 2000.
|
143
|
+
#
|
144
|
+
# (maybe should be revised with some original documentation)
|
145
|
+
module PartiallyMappedCrossover
|
146
|
+
def cross(parent1,parent2)
|
147
|
+
p1, p2 = parent1.genes, parent2.genes
|
148
|
+
raise "Chromosomes too small, should be >= 4" if p1.size < 4
|
149
|
+
|
150
|
+
# Cut-off points must be after first element and before last.
|
151
|
+
cp1 = rand(p1.size-2) + 1
|
152
|
+
cp2 = rand(p1.size-2) + 1 while cp2 == cp1 or cp2.nil?
|
153
|
+
|
154
|
+
of1, of2 = Array.new(p2), Array.new(p1)
|
155
|
+
(cp1..cp2).each do |index|
|
156
|
+
of1.swap_element_at_index!(index, p1[index])
|
157
|
+
of2.swap_element_at_index!(index, p2[index])
|
158
|
+
end
|
159
|
+
|
160
|
+
[of1, of2].map{|of| from_genes(of) }
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
data/lib/charlie/population.rb
CHANGED
@@ -12,7 +12,7 @@ class Population < Array
|
|
12
12
|
replace Array.new(population_size){ genotype_class.new }
|
13
13
|
end
|
14
14
|
|
15
|
-
#
|
15
|
+
# Yields the population and the generation number to block for each generation at a maximum of max_generations times.
|
16
16
|
def evolve_block(max_generations=DEFAULT_MAX_GENS)
|
17
17
|
yield self, 0
|
18
18
|
(max_generations || DEFAULT_MAX_GENS).times {|generation|
|
@@ -91,6 +91,8 @@ class Population < Array
|
|
91
91
|
|
92
92
|
# Effectively runs Population#evolve_until_best multiple times.
|
93
93
|
# * See Population.evolve_multiple_until_population for arguments
|
94
|
+
#
|
95
|
+
# Aliased also as Population.evolve_multiple_until.
|
94
96
|
def self.evolve_multiple_until_best(*args,&b)
|
95
97
|
evolve_multiple_until_population(*args) {|pop| b.call(pop.max) }
|
96
98
|
end
|
data/lib/charlie/selection.rb
CHANGED
@@ -91,8 +91,9 @@ def ScaledRouletteSelection(&block)
|
|
91
91
|
|
92
92
|
if @@index_size != population.size # build index, cache for constant population size
|
93
93
|
@@index_size = population.size
|
94
|
-
|
95
|
-
(0...population.size).map(&@@block).each_with_index{|e,i|
|
94
|
+
index = []
|
95
|
+
(0...population.size).map(&@@block).each_with_index{|e,i| index += Array.new(e.round,i) }
|
96
|
+
@@index = index # ruby 1.9 fix, @@index can't be used in block(?) -- TODO: figure out why
|
96
97
|
end
|
97
98
|
population = population.sort_by(&:fitness)
|
98
99
|
|
@@ -137,17 +138,19 @@ end
|
|
137
138
|
|
138
139
|
|
139
140
|
|
140
|
-
# Tournament selection
|
141
|
-
#
|
141
|
+
# Tournament selection
|
142
|
+
#
|
143
|
+
# Default: selects the 2 individuals with the highest fitness out of a random population with size group_size
|
142
144
|
# and replaces the others with offspring of these 2.
|
143
|
-
#
|
145
|
+
#
|
146
|
+
# Runs the selection for n_times. If n_times == nil (default), it sets it equal to population size / (group_size-2),
|
147
|
+
# i.e. about the same number of new individuals as roulette selection etc.
|
144
148
|
def TournamentSelection(group_size=4,n_times=nil)
|
145
149
|
Module::new{
|
146
150
|
@@group_size = group_size
|
147
151
|
@@n_times = n_times
|
148
152
|
def next_generation(population)
|
149
|
-
|
150
|
-
n_times = @@n_times || (psz / (@@group_size-2))
|
153
|
+
n_times = @@n_times || (population.size / (@@group_size-2))
|
151
154
|
n_times.times{
|
152
155
|
population.shuffle!
|
153
156
|
ix = (0...@@group_size).sort_by{|i| population[i].fitness }
|
@@ -191,8 +194,7 @@ def CoTournamentSelection(group_size=4,full_tournament=false,n_times=nil)
|
|
191
194
|
@@full_tournament = full_tournament
|
192
195
|
@@n_times = n_times
|
193
196
|
def next_generation(population)
|
194
|
-
|
195
|
-
n_times = @@n_times || (psz / @@group_size) #(psz / (@@group_size-2))
|
197
|
+
n_times = @@n_times || (population.size / @@group_size) #(population.size / (@@group_size-2))
|
196
198
|
n_times.times{
|
197
199
|
population.shuffle!
|
198
200
|
points = Array.new(@@group_size,0.0)
|
data/test/test_permutation.rb
CHANGED
@@ -24,6 +24,10 @@ class PermutationTestInsert < PermutationTest
|
|
24
24
|
use InsertionMutator, NullCrossover
|
25
25
|
end
|
26
26
|
|
27
|
+
class PermutationTestPMX < PermutationTest
|
28
|
+
use InsertionMutator, PartiallyMappedCrossover, RandomSelection
|
29
|
+
end
|
30
|
+
|
27
31
|
|
28
32
|
|
29
33
|
class PermTests < Test::Unit::TestCase
|
@@ -47,12 +51,20 @@ class PermTests < Test::Unit::TestCase
|
|
47
51
|
def test_edge_recombination_rand # test if permutation doesn't just stay preserved because of fitness
|
48
52
|
p=nil
|
49
53
|
assert_nothing_raised{
|
50
|
-
klass = Class.new(
|
54
|
+
klass = Class.new(PermutationTestPMX){ use }
|
51
55
|
p=Population.new(klass,20).evolve_silent(20)
|
52
56
|
}
|
53
57
|
p.each{|s| assert_equal s.genes.sort, (0...N).to_a }
|
54
58
|
end
|
55
59
|
|
60
|
+
def test_pmx
|
61
|
+
p=nil
|
62
|
+
assert_nothing_raised{
|
63
|
+
p=Population.new(PermutationTestERO,20).evolve_silent(20)
|
64
|
+
}
|
65
|
+
p.each{|s| assert_equal s.genes.sort, (0...N).to_a }
|
66
|
+
end
|
67
|
+
|
56
68
|
def test_inversion_mutator # test if inversion mutator works
|
57
69
|
p=nil
|
58
70
|
assert_nothing_raised{
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: charlie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sander Land
|
@@ -9,17 +9,18 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-02-12 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: hoe
|
17
|
+
type: :development
|
17
18
|
version_requirement:
|
18
19
|
version_requirements: !ruby/object:Gem::Requirement
|
19
20
|
requirements:
|
20
21
|
- - ">="
|
21
22
|
- !ruby/object:Gem::Version
|
22
|
-
version: 1.
|
23
|
+
version: 1.8.3
|
23
24
|
version:
|
24
25
|
description: "== DESCRIPTION: Charlie is a library for genetic algorithms (GA) and genetic programming (GP). == FEATURES: - Quickly develop GAs by combining several parts (genotype, selection, crossover, mutation) provided by the library. - Sensible defaults are provided with any genotype, so often you only need to define a fitness function. - Easily replace any of the parts by your own code. - Test different strategies in GA, and generate reports comparing them. Example report: http://charlie.rubyforge.org/example_report.html == INSTALL: * sudo gem install charlie == EXAMPLES: This example solves a TSP problem (also quiz #142): N=5 CITIES = (0...N).map{|i| (0...N).map{|j| [i,j] } }.inject{|a,b|a+b} class TSP < PermutationGenotype(CITIES.size) def fitness d=0 (genes + [genes[0]]).each_cons(2){|a,b| a,b=CITIES[a],CITIES[b] d += Math.sqrt( (a[0]-b[0])**2 + (a[1]-b[1])**2 ) } -d # lower distance -> higher fitness. end use EdgeRecombinationCrossover, InversionMutator end Population.new(TSP,20).evolve_on_console(50) This example finds a polynomial which approximates cos(x) class Cos < TreeGenotype([proc{3*rand-1.5},:x], [:-@], [:+,:*,:-]) def fitness -[0,0.33,0.66,1].map{|x| (eval_genes(:x=>x) - Math.cos(x)).abs }.max end use TournamentSelection(4) end Population.new(Cos).evolve_on_console(500)"
|
25
26
|
email: sander.land+ruby@gmail.com
|
@@ -56,6 +57,7 @@ files:
|
|
56
57
|
- examples/function_optimization.rb
|
57
58
|
- examples/matrix.rb
|
58
59
|
- examples/neural.rb
|
60
|
+
- examples/output/bitstring_royalroad.html
|
59
61
|
- examples/output/function_optimization_sombrero.html
|
60
62
|
- examples/output/function_optimization_twopeak.csv
|
61
63
|
- examples/output/function_optimization_twopeak.html
|
@@ -117,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
119
|
requirements: []
|
118
120
|
|
119
121
|
rubyforge_project: charlie
|
120
|
-
rubygems_version: 1.
|
122
|
+
rubygems_version: 1.3.1
|
121
123
|
signing_key:
|
122
124
|
specification_version: 2
|
123
125
|
summary: A genetic algorithms library for Ruby.
|