gegene 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 41e8a0e177bba73f066e17c27edcd7577d43633c
4
+ data.tar.gz: 4c23284c8be074707981392045deb5db19243fe3
5
+ SHA512:
6
+ metadata.gz: 3ff2f8fa8335626a395e0c5aeb4b6b46d8d2cf65ba032773b3fb7e9e51acbc8b163be6e663cb34cc8f875559d990b574b1c155a4f726c61b3d1fe3583d509e20
7
+ data.tar.gz: be8ee9a256f9f2a927b0aa0ad4faf1cb45bf10ba355c1dbdde8390fd5a85abf1f65d009fa3c544961a0c38c27ce80c1cc512c1305e823ea835ba346ecf81599e
@@ -0,0 +1,34 @@
1
+ require 'gegene'
2
+
3
+ class PalindromeGene < Gene
4
+ def initialize(size)
5
+ @size = size
6
+ end
7
+
8
+ # Will raise an error in Gene class if not overloaded here
9
+ def random_allele_value
10
+ prefix = Array.new(@size/2){ rand(97..122).chr}.join("")
11
+ prefix + (@size%2 == 0 ? "" : rand(97..122).chr) +prefix.reverse()
12
+ end
13
+
14
+ # Will raise an error in Gene class if not overloaded here
15
+ def mutate(previous_value)
16
+ self.random_allele_value
17
+ end
18
+ end
19
+
20
+ # The following code is not mandatory
21
+ # use it if you'd rather have Gene.Palyndrome(9) instead of a ctor call
22
+ #class Gene
23
+ # Gene.Palyndrome(size) PalyndromeGene.new(size) end
24
+ #end
25
+
26
+ genome_description = [{palyndrome:PalindromeGene.new(9)}]
27
+
28
+ def fitness(karyotype)
29
+ karyotype[:palyndrome].split(//).select{|c| c =~ /[aeiou]/}.size
30
+ end
31
+
32
+ population = Population.new(25, genome_description, method(:fitness))
33
+ population.evolve(10)
34
+ puts population.karyotypes[0][:palyndrome]
@@ -0,0 +1,34 @@
1
+ require 'gegene'
2
+
3
+ # Follow a genome description for the one max problem:
4
+ genome_description = [
5
+ { v1: Gene.Integer(0,1) },
6
+ { v2: Gene.Integer(0,1) },
7
+ { v3: Gene.Integer(0,1) }
8
+ ]
9
+
10
+ # Here is the fitness function of the one max problem:
11
+ def fitness(karyotype)
12
+ karyotype[:v1] + karyotype[:v2] + karyotype[:v3]
13
+ end
14
+
15
+ # We create a population of six individuals with the previous desc & func:
16
+ population = Population.new(6, genome_description, method(:fitness))
17
+
18
+ # As we known the best solution for the one max problem, we set a
19
+ # fitness target of 3 (1+1+1).
20
+ population.fitness_target = 3
21
+
22
+ # As the population is quite small, we add a little more funk to our
23
+ # evolution process by setting a 30% mutation rate
24
+ population.mutation_rate = 0.3
25
+
26
+ # Let's go for some darwinist fun !
27
+ population.evolve(10)
28
+
29
+ # population.karyotypes is sorted by fitness score, so we can assume that
30
+ # the first element is the fittest
31
+ best_karyotype = population.karyotypes[0]
32
+
33
+ puts "Best karyotype scored #{best_karyotype.fitness}:"
34
+ [:v1, :v2, :v3].each {|x| puts " #{x.to_s}:#{best_karyotype[x]}" }
data/example/simple.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'gegene'
2
+ FITNESS_TARGET = 1 / 0.001
3
+ population = Population.new(
4
+ 50,
5
+ [{a:Gene.Integer(0, 5)},{b:Gene.Integer(-5,5)}],
6
+ lambda {|k| 1 / (0.001 + (12-(k[:a]**2+k[:b])).abs) }
7
+ )
8
+ population.set_mutation_rate(0.5).set_fitness_target(FITNESS_TARGET).evolve(50)
9
+ bk = population.karyotypes[0]
10
+ warn "a:#{bk[:a]} b:#{bk[:b]} => a*a + b = #{bk[:a]**2+bk[:b]}"
data/lib/allele.rb ADDED
@@ -0,0 +1,16 @@
1
+ class Allele
2
+ attr_accessor :value
3
+ def initialize(gene, value)
4
+ @gene = gene
5
+ @value = value
6
+ end
7
+
8
+ def mutate
9
+ @value = @gene.mutate(@value)
10
+ end
11
+
12
+ def copy
13
+ # /!\ if @value is a ref, its underlying object won't be copied
14
+ Allele.new(@gene, @value)
15
+ end
16
+ end
data/lib/chromosome.rb ADDED
@@ -0,0 +1,26 @@
1
+ require 'gene'
2
+ require 'allele'
3
+ class Chromosome
4
+
5
+ def initialize(alleles)
6
+ @alleles = alleles
7
+ end
8
+
9
+ def copy
10
+ Chromosome.new(@alleles.map{|allele| allele.copy })
11
+ end
12
+
13
+ def Chromosome.create_random_from(description)
14
+ Chromosome.new(description.map{|gene| gene.create_random() })
15
+ end
16
+
17
+ def [](gene_position)
18
+ @alleles[gene_position]
19
+ end
20
+
21
+ def mutate
22
+ allele_index = rand @alleles.size
23
+ @alleles[allele_index].mutate()
24
+ end
25
+
26
+ end
data/lib/gegene.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'gene'
2
+ require 'population'
data/lib/gene.rb ADDED
@@ -0,0 +1,72 @@
1
+
2
+ class Gene
3
+ def mutate(allele)
4
+ raise "the 'mutate' function must be overloaded in the inheriting class."
5
+ end
6
+
7
+ def random_allele_value
8
+ raise "the 'random_allele_value' function must be overloaded in the inheriting class."
9
+ end
10
+ def create_random
11
+ Allele.new(self, self.random_allele_value)
12
+ end
13
+ end
14
+
15
+ class NumericGene < Gene
16
+ attr_accessor :min, :max
17
+ def initialize(min, max)
18
+ raise "max (#{max}) must be greater than min (#{min})" if max <= min
19
+ @min = min
20
+ @max = max
21
+ end
22
+
23
+ def mutate(previous_value)
24
+ next_value = random_allele_value
25
+ while next_value == previous_value do
26
+ next_value = random_allele_value
27
+ end
28
+ next_value
29
+ end
30
+ end
31
+
32
+ class IntegerGene < NumericGene
33
+ def initialize(min, max) super(min, max) end
34
+
35
+ def random_allele_value() rand(self.min..self.max) end
36
+ end
37
+
38
+ class FloatGene < NumericGene
39
+ def initialize(min, max) super(min, max) end
40
+
41
+ def random_allele_value
42
+ rand() * (self.max - self.min) + self.min
43
+ end
44
+ end
45
+
46
+ class EnumGene < Gene
47
+ attr_accessor :enum_values
48
+
49
+ def initialize(enum_values)
50
+ raise "EnumGene initialization require an Array" unless enum_values.is_a? Array
51
+ raise "EnumGene require at least two values" unless enum_values.size > 1
52
+ @enum_values = enum_values
53
+ end
54
+
55
+ def random_allele_value
56
+ @enum_values[rand @enum_values.size]
57
+ end
58
+
59
+ def mutate(previous_value)
60
+ new_value = self.random_allele_value
61
+ while new_value == previous_value
62
+ new_value = self.random_allele_value
63
+ end
64
+ new_value
65
+ end
66
+ end
67
+
68
+ class Gene
69
+ def self.Integer(min, max) IntegerGene.new(min, max) end
70
+ def self.Float(min, max) FloatGene.new(min, max) end
71
+ def self.Enum(enum_values) EnumGene.new(enum_values) end
72
+ end
data/lib/genome.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'Karyotype'
2
+
3
+ class Genome
4
+
5
+ def initialize(genome_description)
6
+ raise "Genome description MUST be an Array" unless genome_description.is_a? Array
7
+ @chromosomes_description = []
8
+ @gene_positions = {}
9
+ genome_description.each_with_index do |chomosome_hash, chromosome_position|
10
+ gene_array = []
11
+ chomosome_hash.keys.each_with_index do |gene_name, gene_position|
12
+ @gene_positions[gene_name] = [chromosome_position, gene_position]
13
+ gene_array << chomosome_hash[gene_name]
14
+ end
15
+ @chromosomes_description << gene_array
16
+ end
17
+ end
18
+ attr_accessor :gene_positions
19
+ def get_gene_position(gene_name)
20
+ @gene_positions[gene_name]
21
+ end
22
+
23
+ def create_random_karyotype
24
+ Karyotype.create_random_from(self, @chromosomes_description)
25
+ end
26
+
27
+ end
data/lib/karyotype.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'chromosome'
2
+ class Karyotype
3
+ attr_accessor :chromosomes
4
+ attr_accessor :fitness
5
+ def initialize(genome, chromosomes_description)
6
+ @genome = genome
7
+ @chromosomes_description = chromosomes_description
8
+ end
9
+ private :initialize
10
+ def copy
11
+ karyotype = Karyotype.new(@genome, @chromosomes_description)
12
+ karyotype.chromosomes = self.chromosomes.map{|chromosome| chromosome.copy}
13
+ karyotype
14
+ end
15
+ def to_s
16
+ @genome.gene_positions.keys.map{|gn| "#{gn}=>#{self[gn]}"}.join';'
17
+ end
18
+ def Karyotype.create_random_from(genome, chromosomes_description)
19
+ karyotype = Karyotype.new(genome, chromosomes_description)
20
+ karyotype.chromosomes = chromosomes_description.map {|description|
21
+ Chromosome.create_random_from(description)
22
+ }
23
+ karyotype
24
+ end
25
+
26
+ def [](gene_name)
27
+ return nil if @chromosomes.nil?
28
+ chromosome_position, gene_position = @genome.get_gene_position(gene_name)
29
+ return nil if chromosome_position.nil? || gene_position.nil?
30
+ return @chromosomes[chromosome_position][gene_position].value
31
+ end
32
+
33
+ def +(other)
34
+ child = copy
35
+ other.chromosomes.each_with_index {
36
+ |chromosome, index| child.chromosomes[index] = chromosome if rand(2) == 0
37
+ }
38
+ child
39
+ end
40
+
41
+ def mutate
42
+ @chromosomes[rand @chromosomes.size].mutate
43
+ self
44
+ end
45
+ end
data/lib/population.rb ADDED
@@ -0,0 +1,101 @@
1
+ require 'genome'
2
+
3
+ class Population
4
+ DEFAULT_MUTATION_RATE = 0.01
5
+ DEFAULT_KEEP_ALIVE_RATE = 0.1
6
+ DEFAULT_EVOLVE_ITERATIONS = 1
7
+
8
+ attr_accessor :mutation_rate, :keep_alive_rate, :fitness_target, :karyotypes
9
+
10
+ def evaluate
11
+ @karyotypes.each { |k| k.fitness = @fitness_calculator.call(k) if k.fitness.nil? }
12
+ @karyotypes.sort! { |x,y| y.fitness <=> x.fitness }
13
+ end
14
+
15
+ private :evaluate
16
+
17
+ def initialize(size, genome, fitness_calculator)
18
+ @mutation_rate = DEFAULT_MUTATION_RATE
19
+ @keep_alive_rate = DEFAULT_KEEP_ALIVE_RATE
20
+ @genome = Genome.new(genome)
21
+ @fitness_calculator = fitness_calculator
22
+ @karyotypes = Array.new(size){ @genome.create_random_karyotype }
23
+ evaluate
24
+ end
25
+
26
+ def set_mutation_rate(rate)
27
+ @mutation_rate = rate
28
+ self
29
+ end
30
+
31
+ def set_keep_alive_rate(rate)
32
+ @keep_alive_rate = rate
33
+ self
34
+ end
35
+
36
+ def set_fitness_target(target)
37
+ @fitness_target = target
38
+ self
39
+ end
40
+
41
+ def size
42
+ @karyotypes.size
43
+ end
44
+
45
+ def linear_random_select
46
+ @karyotypes[rand @karyotypes.size]
47
+ end
48
+
49
+ def create_random_mutation
50
+ linear_random_select.copy.mutate
51
+ end
52
+
53
+ private :linear_random_select, :create_random_mutation
54
+
55
+ def random_select
56
+ @karyotypes[
57
+ @karyotypes.size - Integer(Math.sqrt(Math.sqrt(1 + rand(@karyotypes.size**4 - 1))))
58
+ ]
59
+ end
60
+
61
+ def random_breed
62
+ random_select + random_select
63
+ end
64
+
65
+ private :random_select, :random_breed
66
+
67
+ def evolve(iterations = DEFAULT_EVOLVE_ITERATIONS)
68
+ i = 1
69
+ while (i <= iterations) &&
70
+ (@fitness_target.nil? || @fitness_target > @karyotypes[0].fitness) do
71
+ evolve_impl
72
+ i += 1
73
+ end
74
+ self
75
+ end
76
+
77
+ def evolve_impl
78
+ new_population = []
79
+
80
+ # Keeping alive a specific amount of the best karyotypes
81
+ keep_alive_count = Integer(@karyotypes.size * @keep_alive_rate)
82
+ if keep_alive_count > 0 then
83
+ @karyotypes[0, keep_alive_count].each {|karyotype| new_population.push(karyotype)}
84
+ end
85
+
86
+ mutation_count = Integer(@karyotypes.size * @mutation_rate)
87
+ (0..mutation_count-1).each {
88
+ new_population.push create_random_mutation
89
+ }
90
+
91
+ remaining = @karyotypes.size-mutation_count-keep_alive_count
92
+ (0..remaining-1).each {
93
+ child = random_breed
94
+ new_population.push child
95
+ }
96
+ @karyotypes = new_population
97
+ evaluate
98
+ end
99
+ private :evolve_impl
100
+
101
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gegene
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexandre Ignjatovic
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-27 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Framework for fast genetic algorithm development
14
+ email: alexandre.ignjatovic@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/allele.rb
20
+ - lib/chromosome.rb
21
+ - lib/gegene.rb
22
+ - lib/gene.rb
23
+ - lib/genome.rb
24
+ - lib/karyotype.rb
25
+ - lib/population.rb
26
+ - example/adding_gene_type.rb
27
+ - example/one_max.rb
28
+ - example/simple.rb
29
+ homepage: https://github.com/bankair/gegene
30
+ licenses:
31
+ - MIT
32
+ metadata: {}
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project:
49
+ rubygems_version: 2.1.4
50
+ signing_key:
51
+ specification_version: 4
52
+ summary: Genetic algorithm helpers
53
+ test_files: []