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.
@@ -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),
@@ -4,7 +4,7 @@ $:.unshift File.dirname(__FILE__)
4
4
 
5
5
  # This is just a dummy module to avoid making the VERSION constant a global.
6
6
  module Charlie
7
- VERSION = '0.8.0'
7
+ VERSION = '0.8.1'
8
8
  end
9
9
 
10
10
  require 'charlie/etc/monkey'
@@ -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
@@ -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
 
@@ -30,7 +30,7 @@ def NPointCrossover(n=2)
30
30
  }
31
31
  end
32
32
  TwoPointCrossover = NPointCrossover(2)
33
- ThreePointCrossover = NPointCrossover(2)
33
+ ThreePointCrossover = NPointCrossover(3)
34
34
 
35
35
  # Uniform crossover, returns two children.
36
36
  module UniformCrossover
@@ -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
+
@@ -12,7 +12,7 @@ class Population < Array
12
12
  replace Array.new(population_size){ genotype_class.new }
13
13
  end
14
14
 
15
- # yields population and generation number to block each generation, for a maximum of max_generations.
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
@@ -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
- @@index = []
95
- (0...population.size).map(&@@block).each_with_index{|e,i| @@index += Array.new(e.round,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
- # Default: select the 2 individuals with the highest fitness out of a random population with size group_size
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
- # 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.
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
- psz = population.size
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
- psz = population.size
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)
@@ -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(PermutationTestERO){ use RandomSelection }
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.0
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: 2008-02-12 00:00:00 +01:00
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.4.0
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.0.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.