gene_genie 0.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1f634d03e4f8bce6759898a2048719471aff1e0b
4
+ data.tar.gz: ff4ebfd191e17b182149935e1c544b51ceb0f46f
5
+ SHA512:
6
+ metadata.gz: a918e63beb5265d671b2e4b46b28358fb7e57727c590f9b37300eae2b0b57fc7d19abd30b90d0d0a5fea815c6a98bbf1ddb2e65e0b0a2f1e9915fbe88467fa21
7
+ data.tar.gz: 86719523c06e1fd0e35c242385a833c236745d3fe43973494d15cdab567bb43467f34bfda9211f5db1d6f1e00f820109c18f66139115e1252875357e0b2c5346
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ [![Build Status](https://travis-ci.org/MEHColeman/gene_genie.svg?branch=master)](https://travis-ci.org/MEHColeman/gene_genie)
2
+ [![Gem Version](https://badge.fury.io/rb/gene_genie.svg)](http://badge.fury.io/rb/gene_genie)
3
+ [![Code Climate](https://codeclimate.com/github/MEHColeman/gene_genie.png)](https://codeclimate.com/github/MEHColeman/gene_genie)
4
+
5
+ # Gene Genie
6
+
7
+ Hey, I wrote a genetic algorithm gem. Goals:
8
+ * Have fun
9
+ * Be easy and intuitive to use
10
+ * Be open to extension and experimentation
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'gene_genie'
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install gene_genie
25
+
26
+ ## Usage
27
+ Basic usage is designed to be as simple as possible. You provide two things: an exemplar and an evaluator.
28
+ An exemplar is a list of variables along with their possible range of values.
29
+ An evaluator implements a fitness method that returns a numeric value.
30
+ The genetic algorithm will then search for the set of values that maximises the fitness.
31
+
32
+ ```ruby
33
+ require 'gene_genie'
34
+
35
+ exemplar = {
36
+ range_of_ints: 1..10,
37
+ range_of_floats: 1.0..4.5,
38
+ set_of_items: [:apple, :banana, :orange],
39
+ ordered_set_of_items: [:one, :two, :three],
40
+ circular_ordered_set: [:early_morning, :morning, :noon, :afternoon,
41
+ :evening, :midnight]
42
+ }
43
+ ```
44
+
45
+ If you use the simple genie interface, the genetic algorithm will come up with a reasonable best-guesses for various algorthm parameters, but you can dive under the covers to give yourself more flexibility.
46
+ * Population size
47
+ * Gene pools
48
+ * Initialisation
49
+ * Optimisation Criteria
50
+
51
+ Custom objects for crossover, gene selection, etc.
52
+
53
+ ## Contributing
54
+
55
+ 1. Fork it ( https://github.com/MEHColeman/gene_genie/fork )
56
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
57
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
58
+ 4. Push to the branch (`git push origin my-new-feature`)
59
+ 5. Create a new Pull Request
@@ -0,0 +1,38 @@
1
+ module GeneGenie
2
+ # A Gene is the basic unit of the genetic algorithm. Genes hold the
3
+ # information used to evaluate their fitness.
4
+ # They are combined into new Genes during the optimisation process.
5
+ # @since 0.0.1
6
+ class Gene
7
+ def initialize(information, fitness_evaluator)
8
+ @information = information
9
+ @fitness_evaluator = fitness_evaluator
10
+ end
11
+
12
+ def to_hash
13
+ @information
14
+ end
15
+
16
+ def fitness
17
+ @fitness ||= @fitness_evaluator.fitness(@information)
18
+ end
19
+
20
+ def mutate(mutator)
21
+ @information = mutator.call @information
22
+ self
23
+ end
24
+
25
+ def combine(other_gene)
26
+ other_gene_hash = other_gene.to_hash
27
+ new_hash = {}
28
+ @information.each do | k, v |
29
+ new_hash[k] = (rand > 0.5) ? @information[k] : other_gene_hash[k]
30
+ end
31
+ Gene.new(new_hash, @fitness_evaluator)
32
+ end
33
+
34
+ def <=>(gene)
35
+ fitness <=> gene.fitness
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'gene'
2
+
3
+ module GeneGenie
4
+ # GeneFactory
5
+ # This is a helper class that will create a specified number of genes, given
6
+ # a template.
7
+ # The default implementation will produce random genes, but other approaches
8
+ # could be taken.
9
+ class GeneFactory
10
+ def initialize(template, fitness_evaluator)
11
+ @template = template
12
+ @fitness_evaluator = fitness_evaluator
13
+ end
14
+
15
+ def create(size = 1)
16
+ genes = []
17
+ size.times do
18
+ hash = create_hash_from_template
19
+ genes << Gene.new(hash, @fitness_evaluator)
20
+ end
21
+
22
+ genes
23
+ end
24
+
25
+ private
26
+
27
+ def create_hash_from_template
28
+ new_hash = {}
29
+ @template.each do |k, v|
30
+ new_hash[k] = rand(v)
31
+ end
32
+
33
+ new_hash
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,81 @@
1
+ require_relative 'gene_factory'
2
+ require_relative 'mutator/simple_gene_mutator'
3
+ require_relative 'mutator/null_mutator'
4
+
5
+ module GeneGenie
6
+ class GenePool
7
+ def initialize(template, fitness_evaluator, gene_factory,
8
+ mutator = NullMutator.new)
9
+ unless template.instance_of? Hash
10
+ fail ArgumentError, 'template must be a hash of ranges'
11
+ end
12
+ unless fitness_evaluator.respond_to?(:fitness)
13
+ fail ArgumentError, 'fitness_evaluator must respond to fitness'
14
+ end
15
+
16
+ @template = template
17
+ @fitness_evaluator = fitness_evaluator
18
+ @mutator = mutator
19
+
20
+ #size = template_evaluator.recommended_size
21
+ size ||= 10
22
+ @pool = gene_factory.create(size)
23
+ end
24
+
25
+ # build a GenePool with a reasonable set of defaults.
26
+ # You only need to specily the minimum no. of parameters
27
+ def self.build(template, fitness_evaluator)
28
+ gene_mutator = SimpleGeneMutator.new(template)
29
+ gene_factory = GeneFactory.new(template, fitness_evaluator)
30
+ GenePool.new(template, fitness_evaluator, gene_factory,
31
+ gene_mutator)
32
+ end
33
+
34
+ def size
35
+ @pool.size
36
+ end
37
+
38
+ def best
39
+ @pool.max_by { |gene| gene.fitness }
40
+ end
41
+
42
+ def evolve
43
+ old_best_fitness = best.fitness
44
+ new_pool = []
45
+ size.times do
46
+ first_gene, second_gene = select_genes
47
+ new_gene = combine_genes(first_gene, second_gene)
48
+ new_pool << new_gene.mutate(@mutator)
49
+ end
50
+ @pool = new_pool
51
+ best.fitness > old_best_fitness
52
+ end
53
+
54
+ private
55
+ # a very simple selection - pick by sorted order
56
+ # pick two different genes
57
+ def select_genes
58
+ selectees = @pool.sort.reverse
59
+ first, second = nil, nil
60
+ probability = [(( 1.0/size ) * 3), 0.8].min
61
+ while !first || !second do
62
+ selectees.each do |s|
63
+ if rand < probability
64
+ selectees.delete(s)
65
+ if !first
66
+ first = s
67
+ break
68
+ else
69
+ second = s
70
+ end
71
+ end
72
+ end
73
+ end
74
+ [first, second]
75
+ end
76
+
77
+ def combine_genes(first, second)
78
+ first.combine(second)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,67 @@
1
+ require_relative 'gene_pool'
2
+
3
+ # Namespace for GeneGenie genetic algorithm optimisation gem
4
+ # @since 0.0.1
5
+ module GeneGenie
6
+
7
+ # Top level, basic interface for GA optimisation.
8
+ # Genie will attempt to optimise based on best-guess defaults if none are
9
+ # provided
10
+ # @since 0.0.1
11
+ class Genie
12
+
13
+ DEFAULT_NO_OF_GENERATIONS = 50
14
+ IMPROVEMENT_THRESHOLD = 0.1 # %
15
+
16
+ def initialize(template, fitness_evaluator)
17
+ @template = template
18
+ @fitness_evaluator = fitness_evaluator
19
+ @gene_pool = GenePool.build(template, fitness_evaluator)
20
+ end
21
+
22
+ # Optimise the genes until the convergence criteria are met.
23
+ # A reasonable set of defaults for criteria will be applied.
24
+ # @param [Integer] number_of_generations
25
+ def optimise(number_of_generations = 0)
26
+ previous_best = best_fitness
27
+
28
+ # optimise
29
+ if number_of_generations > 0
30
+ evolve_n_times(number_of_generations)
31
+ else
32
+ optimise_by_strategy
33
+ end
34
+
35
+ @best_fitness = @fitness_evaluator.fitness(best)
36
+
37
+ @best_fitness > previous_best
38
+ end
39
+ alias_method :optimize, :optimise
40
+
41
+ def best
42
+ @gene_pool.best.to_hash
43
+ end
44
+
45
+ def best_fitness
46
+ @gene_pool.best.fitness
47
+ end
48
+
49
+ private
50
+ def evolve_n_times(n)
51
+ n.times { @gene_pool.evolve }
52
+ end
53
+
54
+ def optimise_by_strategy
55
+ DEFAULT_NO_OF_GENERATIONS.times do
56
+ current_fitness = best_fitness
57
+ @gene_pool.evolve
58
+ end
59
+ DEFAULT_NO_OF_GENERATIONS.times do
60
+ current_fitness = best_fitness
61
+ @gene_pool.evolve
62
+ break if best_fitness < current_fitness *
63
+ (1 + (IMPROVEMENT_THRESHOLD / 100 ))
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,12 @@
1
+ module GeneGenie
2
+ # A NullMutator does no mutation on the gene
3
+ # @since 0.0.1
4
+ class NullMutator
5
+ def initialize(*_)
6
+ end
7
+
8
+ def call(hash)
9
+ hash
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ module GeneGenie
2
+ # A SimpleGeneMutator loops through each member of a hash, and has a 1%
3
+ # chance of swapping the value for another valid value (based on the
4
+ # template)
5
+ # @since 0.0.1
6
+ class SimpleGeneMutator
7
+ def initialize(template, mutation_rate = 0.01)
8
+ @template = template
9
+ @mutation_rate = mutation_rate
10
+ end
11
+
12
+ def call(hash)
13
+ hash.each do |k, v|
14
+ if rand < @mutation_rate
15
+ hash[k] = rand(@template[k])
16
+ end
17
+ end
18
+ hash
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,3 @@
1
+ module GeneGenie
2
+ VERSION = '0.0.1'
3
+ end
data/lib/gene_genie.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative 'gene_genie/genie'
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gene_genie
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mark Coleman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-spec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: JUST A PROTOTYPE WORK IN PROGRESS! Optimise anything that responds to
70
+ 'fitness' and takes a hash
71
+ email:
72
+ - m@rkcoleman.co.uk
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - README.md
78
+ - lib/gene_genie.rb
79
+ - lib/gene_genie/gene.rb
80
+ - lib/gene_genie/gene_factory.rb
81
+ - lib/gene_genie/gene_pool.rb
82
+ - lib/gene_genie/genie.rb
83
+ - lib/gene_genie/mutator/null_mutator.rb
84
+ - lib/gene_genie/mutator/simple_gene_mutator.rb
85
+ - lib/gene_genie/version.rb
86
+ homepage: https://github.com/MEHColeman/gene_genie
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.4.5
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Genetic algorithm optimisation gem
110
+ test_files: []