charlie 0.5.0 → 0.6.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 +11 -0
- data/Manifest.txt +5 -1
- data/README.txt +33 -43
- data/Rakefile +30 -5
- data/TODO.txt +11 -9
- data/data/CROSSOVER +6 -1
- data/data/GENOTYPE +5 -2
- data/data/MUTATION +5 -0
- data/data/SELECTION +2 -3
- data/examples/money.rb +35 -0
- data/examples/output/flattened_sombero.html +3849 -3849
- data/examples/output/flattened_sombero2_.html +2321 -2321
- data/examples/output/fopt1_dblopt.html +1276 -1276
- data/examples/output/hill10.html +3906 -3906
- data/examples/output/hill2.csv +24 -24
- data/examples/output/hill2.html +177 -177
- data/examples/output/royalroad1_report.html +531 -531
- data/examples/output/royalroad2_report.html +592 -592
- data/examples/output/royalroadquick_report.html +243 -243
- data/examples/output/tsp.html +947 -403
- data/examples/output/weasel1_report.html +616 -616
- data/examples/output/weasel2_report.html +115 -115
- data/examples/tree.rb +90 -0
- data/examples/tsp.rb +19 -12
- data/lib/charlie/etc/monkey.rb +28 -44
- data/lib/charlie/genotype.rb +30 -1
- data/lib/charlie/permutation/permutation.rb +52 -1
- data/lib/charlie/population.rb +58 -20
- data/lib/charlie/selection.rb +1 -1
- data/lib/charlie/tree/tree.rb +128 -0
- data/lib/charlie.rb +2 -5
- data/test/t_common.rb +18 -2
- data/test/test_benchmark.rb +12 -10
- data/test/test_evolve.rb +87 -0
- data/test/test_permutation.rb +37 -3
- data/test/test_tree.rb +57 -0
- metadata +69 -50
- data/test/test_basic.rb +0 -32
data/History.txt
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
== 0.6.0 / 2007-12-26
|
2
|
+
* Some fixes for some changes in Ruby 1.9
|
3
|
+
* Edge recombination crossover added.
|
4
|
+
* Inversion mutator added.
|
5
|
+
* Removed duplicate code in Population#evolve_* methods, and added evolve_until_* methods.
|
6
|
+
* Using allocate instead of new in Genotype#from_genes.
|
7
|
+
* Added Genotype::cache_fitness
|
8
|
+
* Added Tree based genotypes / genetic programming.
|
9
|
+
* Moved Class#use to Genotype::use
|
10
|
+
* Better defaults for benchmarks and a fix for csv output.
|
11
|
+
|
1
12
|
== 0.5.0 / 2007-12-19
|
2
13
|
* First release.
|
3
14
|
|
data/Manifest.txt
CHANGED
@@ -15,6 +15,7 @@ examples/function_opt_sombero.rb
|
|
15
15
|
examples/gladiatorial_simple.rb
|
16
16
|
examples/gladiatorial_sunburn.rb
|
17
17
|
examples/gridwalk.rb
|
18
|
+
examples/money.rb
|
18
19
|
examples/output/flattened_sombero.html
|
19
20
|
examples/output/flattened_sombero2_.html
|
20
21
|
examples/output/fopt1_dblopt.html
|
@@ -30,6 +31,7 @@ examples/output/weasel2_report.html
|
|
30
31
|
examples/royalroad.rb
|
31
32
|
examples/royalroad2.rb
|
32
33
|
examples/simple_climb_hill2.rb
|
34
|
+
examples/tree.rb
|
33
35
|
examples/tsp.rb
|
34
36
|
examples/weasel.rb
|
35
37
|
lib/charlie.rb
|
@@ -44,10 +46,12 @@ lib/charlie/mutate.rb
|
|
44
46
|
lib/charlie/permutation/permutation.rb
|
45
47
|
lib/charlie/population.rb
|
46
48
|
lib/charlie/selection.rb
|
49
|
+
lib/charlie/tree/tree.rb
|
47
50
|
test/t_common.rb
|
48
|
-
test/test_basic.rb
|
49
51
|
test/test_benchmark.rb
|
50
52
|
test/test_cross.rb
|
53
|
+
test/test_evolve.rb
|
51
54
|
test/test_mutator.rb
|
52
55
|
test/test_permutation.rb
|
53
56
|
test/test_sel.rb
|
57
|
+
test/test_tree.rb
|
data/README.txt
CHANGED
@@ -1,53 +1,18 @@
|
|
1
|
-
Charlie
|
2
|
-
|
1
|
+
== Charlie version 0.6.0
|
3
2
|
* http://rubyforge.org/projects/charlie/
|
4
3
|
* http://charlie.rubyforge.org
|
5
4
|
* mailto:sander.land+ruby@gmail.com
|
6
5
|
|
7
6
|
== DESCRIPTION:
|
8
|
-
Charlie is a library for genetic algorithms
|
9
|
-
and run genetic algorithms. You can choose selection, crossover or mutation
|
10
|
-
strategies from either built-in options or simply write your own.
|
11
|
-
It also includes methods that can be used to compare several of these
|
12
|
-
strategies, generating reports with statistics that can be used to determine
|
13
|
-
which one to use in future problems.
|
14
|
-
|
15
|
-
== EXAMPLES:
|
16
|
-
This example finds the binary representation of the number 512.
|
17
|
-
require 'rubygems'
|
18
|
-
require 'charlie'
|
19
|
-
class Find512 < BitStringGenotype(10) # choose a genotype, in this case a list of 10 bits represents a solution
|
20
|
-
# Define a fitness function. This one returns minus the offset to the best solution, so a higher number is better.
|
21
|
-
# Usually, you won't know the best solution, and will define this as some value that needs to be maximized.
|
22
|
-
def fitness
|
23
|
-
# Use the 'genes' function to retrieve the array of bits representing this solution.
|
24
|
-
-(genes.map(&:to_s).join.to_i(2) - 512).abs
|
25
|
-
end
|
26
|
-
end
|
27
|
-
# Finally, create an instance of a population (with the default size of 20) and let it run for the default number of 100 generations.
|
28
|
-
Population.new(Find512).evolve_on_console
|
29
|
-
|
30
|
-
This example solves RubyQuiz #142.
|
31
|
-
|
32
|
-
require 'rubygems'
|
33
|
-
require 'charlie'
|
34
|
-
N=5
|
35
|
-
CITIES = (0...N).map{|i| (0...N).map{|j| [i,j] } }.inject{|a,b|a+b}
|
7
|
+
Charlie is a library for genetic algorithms (GA) and genetic programming (GP).
|
36
8
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
d += Math.sqrt( (a[0]-b[0])**2 + (a[1]-b[1])**2 )
|
43
|
-
}
|
44
|
-
-d # lower distance -> higher fitness.
|
45
|
-
end
|
46
|
-
end
|
47
|
-
pop = Population.new(TSP,20).evolve_on_console(50)
|
9
|
+
== FEATURES:
|
10
|
+
- Quickly develop GAs by combining several parts (genotype, selection, crossover, mutation) provided by the library.
|
11
|
+
- Sensible defaults are provided with any genotype, so often you only need to define a fitness function.
|
12
|
+
- Easily replace any of the parts by your own code.
|
13
|
+
- Test different strategies in GA, and generate reports comparing them. Example report: http://charlie.rubyforge.org/example_report.html
|
48
14
|
|
49
15
|
== INSTALL:
|
50
|
-
|
51
16
|
* sudo gem install charlie
|
52
17
|
|
53
18
|
== Documentation
|
@@ -55,13 +20,38 @@ Because of the high amount of metaprogramming used in the package, the rdoc docu
|
|
55
20
|
|
56
21
|
The following pages contain overviews of the most important parts, including pointers to the appropriate pages of the documentation.
|
57
22
|
* Genotypes[link:files/data/GENOTYPE.html]
|
23
|
+
* Population
|
58
24
|
* Selection[link:files/data/SELECTION.html]
|
59
25
|
* Crossover[link:files/data/CROSSOVER.html]
|
60
26
|
* Mutation[link:files/data/MUTATION.html]
|
61
27
|
* Benchmarking[link:files/data/BENCHMARK.html]
|
28
|
+
Also see the 'examples' directory included in the tarball or gem for several examples, where most of the functionality is used.
|
62
29
|
|
63
|
-
|
30
|
+
== EXAMPLES:
|
31
|
+
This example solves a TSP problem (also quiz #142):
|
32
|
+
N=5
|
33
|
+
CITIES = (0...N).map{|i| (0...N).map{|j| [i,j] } }.inject{|a,b|a+b}
|
34
|
+
class TSP < PermutationGenotype(CITIES.size)
|
35
|
+
def fitness
|
36
|
+
d=0
|
37
|
+
(genes + [genes[0]]).each_cons(2){|a,b|
|
38
|
+
a,b=CITIES[a],CITIES[b]
|
39
|
+
d += Math.sqrt( (a[0]-b[0])**2 + (a[1]-b[1])**2 )
|
40
|
+
}
|
41
|
+
-d # lower distance -> higher fitness.
|
42
|
+
end
|
43
|
+
use EdgeRecombinationCrossover, InversionMutator
|
44
|
+
end
|
45
|
+
Population.new(TSP,20).evolve_on_console(50)
|
64
46
|
|
47
|
+
This example finds a polynomial which approximates cos(x)
|
48
|
+
class Cos < TreeGenotype([proc{3*rand-1.5},:x], [:+,:*,:-], [:-@])
|
49
|
+
def fitness
|
50
|
+
-[0,0.33,0.66,1].map{|x| (eval_genes(:x=>x) - Math.cos(x)).abs }.max
|
51
|
+
end
|
52
|
+
use TournamentSelection(4)
|
53
|
+
end
|
54
|
+
Population.new(Cos).evolve_on_console(500)
|
65
55
|
|
66
56
|
|
67
57
|
== LICENSE:
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ $LOAD_PATH.unshift './lib'
|
|
6
6
|
|
7
7
|
require 'charlie'
|
8
8
|
|
9
|
-
Hoe.new('charlie', Charlie::VERSION) do |p|
|
9
|
+
h = Hoe.new('charlie', Charlie::VERSION) do |p|
|
10
10
|
p.rubyforge_name = 'charlie'
|
11
11
|
p.url = 'http://charlie.rubyforge.org'
|
12
12
|
|
@@ -14,24 +14,28 @@ Hoe.new('charlie', Charlie::VERSION) do |p|
|
|
14
14
|
p.email = 'sander.land+ruby@gmail.com'
|
15
15
|
|
16
16
|
p.summary = 'A genetic algorithms library for Ruby.'
|
17
|
-
p.description = p.paragraphs_of('README.txt',
|
17
|
+
p.description = [1,2,6,7].map{|n| p.paragraphs_of('README.txt', n)}.join("\n\n")
|
18
18
|
|
19
|
-
|
20
19
|
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
21
20
|
|
22
21
|
#p.test_globs ['test/test_all.rb'] # removed test_all, because rake now does that anyway
|
23
22
|
p.clean_globs = ['test/output/*','./**/*~'] # Remove this on "rake clean" : test output and kate backups
|
24
23
|
|
25
|
-
p.rdoc_pattern = /^lib|\.txt$|^data\/[A-Z]
|
24
|
+
p.rdoc_pattern = /^lib|\.txt$|^data\/[A-Z]+(\..+)?$/ # include data/* doc files
|
26
25
|
p.remote_rdoc_dir = '' # Release to root
|
26
|
+
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
h.spec.extra_rdoc_files << 'data/GENOTYPE' << 'data/SELECTION' << 'data/CROSSOVER' << 'data/MUTATION' << 'data/BENCHMARK' # rubygems ignores files on install w/o this?
|
30
|
+
h.spec.rdoc_options << "--inline-source" << "--line-numbers"
|
31
|
+
|
32
|
+
task :build_manifest => [:clean] do |t|
|
30
33
|
require 'find'
|
31
34
|
paths = []
|
32
35
|
Find.find(".") do |path|
|
33
36
|
next if File.directory?(path)
|
34
37
|
next if path =~ /\.svn/ # no svn
|
38
|
+
next if path =~ /misc-not-gem/ # other stuff not included
|
35
39
|
next if path =~ /~$/ # no kate backups
|
36
40
|
paths << path.sub(%r{^\./}, '')
|
37
41
|
end
|
@@ -41,3 +45,24 @@ task :build_manifest do |t|
|
|
41
45
|
end
|
42
46
|
|
43
47
|
|
48
|
+
task :show_desc do |t|
|
49
|
+
puts h.summary
|
50
|
+
puts "-"*75
|
51
|
+
puts h.description
|
52
|
+
puts "-"*75
|
53
|
+
puts h.changes
|
54
|
+
end
|
55
|
+
|
56
|
+
Rake::RDocTask.new(:docs) do |rd|
|
57
|
+
rd.main = "README.txt"
|
58
|
+
|
59
|
+
rd.rdoc_dir = 'doc'
|
60
|
+
files = h.spec.files.grep(h.rdoc_pattern)
|
61
|
+
files -= ['Manifest.txt']
|
62
|
+
rd.rdoc_files.push(*files)
|
63
|
+
|
64
|
+
rd.title = "Documentation for charlie-#{Charlie::VERSION} - A genetic algorithms library for Ruby."
|
65
|
+
rd.options << "--inline-source" << "--line-numbers"
|
66
|
+
end
|
67
|
+
|
68
|
+
|
data/TODO.txt
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
This is my todo list. Contributions and suggestions don't have to be restricted to these things in any way.
|
2
2
|
|
3
3
|
=== Small stuff
|
4
|
-
* fix the initialize call in Genotype#from_genes
|
5
4
|
* clean up PCross, PMutate
|
6
5
|
* n-point crossover for lists
|
7
|
-
|
6
|
+
* external elitism (or whatever this is called). apply elitism but do not allow the elite to reproduce.
|
7
|
+
* move benchmark method(s) to separate class(?)
|
8
8
|
=== Bigger stuff
|
9
|
-
* Better crossover and/or mutation operators for permutations. Especially something useful for TSP. (http://en.wikipedia.org/wiki/Edge_recombination_operator ?)
|
10
9
|
* More builtin genotypes:
|
11
|
-
1. Tree-based, extending into genetic programming.
|
12
|
-
2. Graphs / adjacency matrix.
|
13
|
-
3. Neural networks
|
14
|
-
|
10
|
+
1. Tree-based, extending into genetic programming. (partially done, mutation options to be extended)
|
11
|
+
2. Graphs / adjacency matrix. -- or multidimensional arrays in general?
|
12
|
+
3. Neural networks, Perceptron
|
13
|
+
* Method for population to do several shorter runs until convergence.
|
14
|
+
* Method for population to do several shorter runs, save some the best in each to generate a new population, and evolve this. is this useful?
|
15
|
+
* More selection algorithms. Multiple populations w migration? niching?
|
15
16
|
* Extend benchmarking options:
|
16
|
-
1.
|
17
|
-
2.
|
17
|
+
1. Extend dsl to have options for manual test cases given by sel/cross/mut/etc options, or even free code blocks.
|
18
|
+
2. Option to compare several generation/population size settings. Especially both of them together while keeping the number of fitness evaluations roughly constant.
|
19
|
+
3. Detailed stats for use in comparing a small number of strategies? Including population stats for every generation and run, graphs?
|
18
20
|
|
19
21
|
|
20
22
|
|
data/data/CROSSOVER
CHANGED
@@ -14,7 +14,7 @@ The crossover operator should be defined as a +cross+ method in the metaclass of
|
|
14
14
|
Also see the Genotype#from_genes function, which can be useful for generating children after extracting and recombining the parents' genes.
|
15
15
|
|
16
16
|
The builtin crossover operators are implemented as modules which should be included in the metaclass.
|
17
|
-
Using the
|
17
|
+
Using the Genotype.use keyword does this automatically.
|
18
18
|
|
19
19
|
== General Crossovers
|
20
20
|
=== NullCrossover
|
@@ -35,6 +35,11 @@ Standard uniform crossover.Returns two children.
|
|
35
35
|
PermutationCrossover is a partial preservation crossover for permutations.
|
36
36
|
It is fairly destructive, which can lead to poor performance.
|
37
37
|
|
38
|
+
EdgeRecombinationCrossover is a less destructive crossover for permutations.
|
39
|
+
It's reasonably effective, but fairly slow and only works for permutations of 0...n, so you have to look up the actual elements in the fitness function.
|
40
|
+
|
41
|
+
=== For TreeGenotype
|
42
|
+
TreeCrossover does a standard subtree swapping crossover for trees. Returns two children.
|
38
43
|
|
39
44
|
== Meta-crossovers
|
40
45
|
These functions take one or more crossover modules and generate a new crossover module.
|
data/data/GENOTYPE
CHANGED
@@ -4,7 +4,7 @@ This is usually an array of numbers or bits.
|
|
4
4
|
|
5
5
|
=== Creating your own.
|
6
6
|
If you are creating your own genotype, just inherit from the base Genotype class.
|
7
|
-
You should initialize an instance in the +initialize+ method, and make sure the +genes+ and
|
7
|
+
You should initialize an instance in the +initialize+ method, and make sure the +genes+ and <tt>genes=</tt> functions work properly.
|
8
8
|
|
9
9
|
The base class includes NullCrossover, NullMutator and Elitism(ScaledRouletteSelection(),1) by default.
|
10
10
|
|
@@ -43,7 +43,10 @@ These genotypes have their own crossover and mutation operators.
|
|
43
43
|
==== PermutationGenotype(n,elements=0...n) (file)[link:files/lib/charlie/permutation/permutation_rb.html]
|
44
44
|
Genotype for permutations. Includes PermutationMutator and PermutationCrossover by default.
|
45
45
|
|
46
|
-
|
46
|
+
==== TreeGenotype(terminals = [proc{rand},:x], binary_ops = [:+,:*,:-,:/], unary_ops = nil) (file)[link:files/lib/charlie/tree/tree_rb.html]
|
47
|
+
* Genotype for trees. Useful for genetic programming. Includes TreeMutator and TreeCrossover by default.
|
48
|
+
* Functions included: eval_genes(terminal_values_hash), size, module GPTreeHelper
|
49
|
+
* Check examples/tree.rb for usage.
|
47
50
|
|
48
51
|
|
49
52
|
|
data/data/MUTATION
CHANGED
@@ -33,6 +33,11 @@ Both of these parameters use Symbol#[] in the examples. <tt>:replace['a','b']</t
|
|
33
33
|
=== For PermutationGenotype
|
34
34
|
PermutationMutator is a transposition mutator for permutations.
|
35
35
|
|
36
|
+
InversionMutator is another mutator for permutations. It inverses a part of the genes array.
|
37
|
+
|
38
|
+
=== For TreeGenotype
|
39
|
+
TreeMutator replaces a randomly chosen subtree with a new, randomly generated, subtree of depth <= 2.
|
40
|
+
|
36
41
|
== Meta-mutators
|
37
42
|
These functions take one or more mutator modules and generate a new mutator module.
|
38
43
|
|
data/data/SELECTION
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
== Selection documentation
|
2
2
|
A selection operator generates the new generation from the old.
|
3
3
|
|
4
|
-
|
5
|
-
|
6
4
|
The selection strategy should be defined as a +next_generation+ method in the metaclass of your genotype class.
|
7
5
|
class Example < Genotype
|
8
6
|
...
|
@@ -14,6 +12,8 @@ The selection strategy should be defined as a +next_generation+ method in the me
|
|
14
12
|
use RouletteSelection # or include some builtin operator
|
15
13
|
end
|
16
14
|
|
15
|
+
The builtin selection operators are implemented as modules which should be included in the metaclass.
|
16
|
+
Using the Genotype.use keyword does this automatically.
|
17
17
|
|
18
18
|
== Fitness-based selection strategies
|
19
19
|
These strategies work with genotype classes which define a +fitness+ function. The +fitness+ function should return a number, higher fitness means better and more likely to be selected. All except RouletteSelection can handle negative fitness values.
|
@@ -36,7 +36,6 @@ Generates a module which applies elitism to some selection strategy. This:
|
|
36
36
|
This ensures the maximum fitness can never decrease.
|
37
37
|
|
38
38
|
|
39
|
-
|
40
39
|
== Co-evolutionary selection strategies
|
41
40
|
These strategies work with genotype classes which define a <tt>fight(other)</tt> function.
|
42
41
|
This function should return true if the instance defeats +other+ in a 'fight'.
|
data/examples/money.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
require '../lib/charlie'
|
3
|
+
|
4
|
+
module MoneyBase
|
5
|
+
def words
|
6
|
+
s, e, n, d, m, o, r, y = genes # first 8 genes represent the 8 unique characters
|
7
|
+
["#{s}#{e}#{n}#{d}","#{m}#{o}#{r}#{e}","#{m}#{o}#{n}#{e}#{y}"].map(&:to_i)
|
8
|
+
end
|
9
|
+
def fitness
|
10
|
+
send,more,money = words
|
11
|
+
return -1e6 if send <= 999 || more <= 999 # no cheating by setting s or m to 0
|
12
|
+
return -(money - (send+more)).abs
|
13
|
+
end
|
14
|
+
def to_s
|
15
|
+
send,more,money = words
|
16
|
+
"#{send}+#{more}=#{money}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# send+more=money problem as a string genotype
|
21
|
+
# remember, string genotypes are just arrays with elements from some set, not necessarily chars.
|
22
|
+
class Money < StringGenotype(8,0..9)
|
23
|
+
include MoneyBase
|
24
|
+
def fitness # no permutation, so need to have a penalty for reusing chars
|
25
|
+
super - 100*(8 - genes.uniq.size)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
Population.new(Money).evolve_on_console
|
29
|
+
|
30
|
+
class MoneyP < PermutationGenotype(10) # send+more=money problem as a permutation
|
31
|
+
include MoneyBase
|
32
|
+
end
|
33
|
+
|
34
|
+
Population.new(MoneyP).evolve_on_console
|
35
|
+
|