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 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. It allows you to easily create
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
- class TSP < PermutationGenotype(CITIES.size)
38
- def fitness
39
- d=0
40
- (genes + [genes[0]]).each_cons(2){|a,b|
41
- a,b=CITIES[a],CITIES[b]
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
- Also see the 'examples' directory for several examples, where most of the functionality is used.
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', 2..3).join("\n\n")
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
- task :build_manifest do |t|
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
- 4. selection algorithms.
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. Compare several generation/population size settings. Especially both of them together while keeping the number of fitness evaluations roughly constant.
17
- 2. Detailed stats for use in comparing a small number of strategies? Including population stats for every generation and run, graphs?
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 Class#use keyword does this automatically.
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 +genes=+ functions work properly.
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
+