evolvable 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0c306e82d21ec726d59896acd642596cc3eb16afc7f9b4e6139cdfd518a4575
4
- data.tar.gz: e39f962084249d83e96b0900f2b6851c29f56f1c7656f9a4556278de534b0b5c
3
+ metadata.gz: 833387bfa4abc039a65f181a9a31cf582075d8e90a7bef1f483ca4a9fa53c699
4
+ data.tar.gz: f2b32861840ae9c7fe89f5353ab535b2966da0592a1c619f01adecc2be1629e6
5
5
  SHA512:
6
- metadata.gz: ac75175b693ff6733208b1e5c118ccc577dfebae13c498b613ab440a38136255299914ce6017bce64dcb4b065e5acd59b71c1e3047bb2210daa93ea1770745dd
7
- data.tar.gz: 2f1c0e5df3868e255db1ebea18160fdd246604d9a54c1834279421d494a6701c9f9803cb2aacf7273fcbd0cf2141d8013813b8381427de330c078dac617e2415
6
+ metadata.gz: 667ab941471ab3b949ec631b2d94115667447d43736502862439c574e1c8e1d8231cd37c64aea2bc2900ea750054a17e3e58d7064c030250c5e0792c4019f9b5
7
+ data.tar.gz: a5c40439db48c8205bd12749ecf103e70266079ecde14eedbc45b180a10154b9c4e025a323411ff6c79b28603a744b4f59f9f5516785e90d0e3fccef9cc8ad05
data/README.md CHANGED
@@ -1,8 +1,119 @@
1
1
  # Evolvable
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/evolvable`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Genetic algorithms mimic biological processes such as natural selection, crossover, and mutation to model evolutionary behaviors in code. The "evolvable" gem can add evolutionary behavior to any Ruby object.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ If you're interested in seeing exactly how the "evolvable" gem evolves populations of Ruby objects, I suggest opening up the [Population](https://github.com/mattruzicka/evolvable/blob/master/lib/evolvable/population.rb) class, specifically check out the following methods:
6
+
7
+ - evolve!
8
+ - evaluate_objects!
9
+ - select_objects!
10
+ - crossover_objects!
11
+ - mutate_objects!
12
+
13
+ ## Usage
14
+
15
+ To introduce evolvable behavior to any Ruby object, do the following:
16
+
17
+ 1. Include the Evolvable module
18
+ 2. Define a class method named "evolvable_gene_pool"
19
+ 3. Define an instance method named "fitness"
20
+
21
+ For example, let's say we want to make a text-to-speech command evolve from saying random nonsense to saying whatever you desire.
22
+
23
+ ```Ruby
24
+ require 'evolvable'
25
+
26
+ class Sentence
27
+ include Evolvable
28
+
29
+ DICTIONARY = ('a'..'z').to_a << ' '
30
+ DESIRED_WORDS = 'colorless green ideas sleep furiously'
31
+
32
+ def self.evolvable_gene_pool
33
+ Array.new(DESIRED_WORDS.length) { |index| [index, DICTIONARY] }
34
+ end
35
+
36
+ def fitness
37
+ score = 0
38
+ @genes.values.each_with_index do |value, index|
39
+ score += 1 if value == DESIRED_WORDS[index]
40
+ end
41
+ score
42
+ end
43
+
44
+ def words
45
+ @genes.values.join
46
+ end
47
+ end
48
+ ```
49
+
50
+ Now let's listen to our computer evolve its speech. The following code assumes your system has a text-to-speech command named "say" installed. Run this code in irb:
51
+
52
+ ```ruby
53
+ population = Sentence.evolvable_population
54
+ object = population.strongest_object
55
+
56
+ until object.fitness == Sentence::DESIRED_WORDS.length
57
+ puts object.words
58
+ system("say #{object.words}")
59
+ population.evolve!(fitness_goal: Sentence::DESIRED_WORDS.length)
60
+ object = population.strongest_object
61
+ end
62
+ ```
63
+
64
+ To play with a more interactive version, check out https://github.com/mattruzicka/evolvable_sentence
65
+
66
+ ### The Gene Pool
67
+
68
+ TODO: add descriptions and examples for following
69
+
70
+ *.evolvable_gene_pool*
71
+ *.evolvable_genes_count*
72
+
73
+ ### Fitness
74
+
75
+ TODO: add description and example
76
+
77
+ ### Custom Evolvable Class Methods
78
+
79
+ TODO: add descriptions and example implementations for the following
80
+
81
+ *.evolvable_evaluate!(objects)*
82
+ *.evolvable_population(args = {})*
83
+ *.evolvable_population_attrs*
84
+ *.evolvable_initialize(genes, population, object_index)*
85
+
86
+ ### Hooks
87
+
88
+ TODO: add description
89
+
90
+ *.evolvable_before_evolution(population)*
91
+ *.evolvable_after_select(population)*
92
+ *.evolvable_after_evolution(population)*
93
+
94
+ ### Custom Mutations
95
+
96
+ TODO: Show how to define and use a custom mutation object
97
+
98
+ ### Custom Crossover
99
+
100
+ TODO: Show how to define and use a custom crossover object
101
+
102
+ ### Helper Methods
103
+
104
+ TODO: add description
105
+
106
+ *Evolvable.combine_dimensions(dimensions)*
107
+
108
+ ### Configuration
109
+
110
+ TODO: Make logger configurable and make it smarter about picking a default
111
+
112
+ ## Demos
113
+
114
+ - https://github.com/mattruzicka/evolvable_sentence - A more interactive version of the evolvable sentence code above.
115
+ - https://github.com/mattruzicka/evolvable_equation - Evolves an equation of a specified length to evaluate to a given number.
116
+ - More demos to come.
6
117
 
7
118
  ## Installation
8
119
 
@@ -20,15 +131,15 @@ Or install it yourself as:
20
131
 
21
132
  $ gem install evolvable
22
133
 
23
- ## Usage
134
+ ## Development
24
135
 
25
- TODO: Write usage instructions here
136
+ After checking out the repo, run `bundle install` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
26
137
 
27
- ## Development
138
+ I am looking to both simplify how genes are defined as well as support more complex gene types by way of a new Gene class. Currently, genes are represented as an array of arrays or hashes depending on the context. This will likely change.
28
139
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
140
+ I would also like to make more obvious how an evolvable object's genes can influence its behavior/fitness with well-defined pattern for gene expression, probably via an instance method on a gene called "express".
30
141
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
142
+ I have a general sense of how I want to move forward on these features, but feel free to message me with ideas or implementations.
32
143
 
33
144
  ## Contributing
34
145
 
data/evolvable.gemspec CHANGED
@@ -7,8 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.version = Evolvable::VERSION
8
8
  spec.authors = ['Matt Ruzicka']
9
9
 
10
- spec.summary = 'A framework for writing genetic algorithms'
11
- spec.summary = 'Make Ruby objects evolvable with genetic algorithms'
10
+ spec.summary = 'Add evolutionary behavior to any Ruby object'
12
11
  spec.homepage = 'https://github.com/mattruzicka/evolvable'
13
12
  spec.license = 'MIT'
14
13
 
@@ -1,5 +1,5 @@
1
1
  module Evolvable
2
- module Callbacks
2
+ module Hooks
3
3
  def evolvable_before_evolution(population); end
4
4
 
5
5
  def evolvable_after_select(population); end
@@ -11,18 +11,18 @@ module Evolvable
11
11
  :evolvable_gene_pool_size,
12
12
  :evolvable_random_genes
13
13
 
14
- def call!(individuals)
15
- @evolvable_class = individuals.first.class
16
- mutations_count = find_mutations_count(individuals)
14
+ def call!(objects)
15
+ @evolvable_class = objects.first.class
16
+ mutations_count = find_mutations_count(objects)
17
17
  return if mutations_count.zero?
18
18
 
19
19
  mutant_genes = generate_mutant_genes(mutations_count)
20
- individual_mutations_count = mutations_count / individuals.count
21
- individual_mutations_count = 1 if individual_mutations_count.zero?
20
+ object_mutations_count = mutations_count / objects.count
21
+ object_mutations_count = 1 if object_mutations_count.zero?
22
22
 
23
- mutant_genes.each_slice(individual_mutations_count).with_index do |m_genes, index|
24
- individual = individuals[index] || individuals.sample
25
- genes = individual.genes
23
+ mutant_genes.each_slice(object_mutations_count).with_index do |m_genes, index|
24
+ object = objects[index] || objects.sample
25
+ genes = object.genes
26
26
  genes.merge!(m_genes.to_h)
27
27
  rm_genes_count = genes.count - evolvable_genes_count
28
28
  genes.keys.sample(rm_genes_count).each { |key| genes.delete(key) }
@@ -40,10 +40,10 @@ module Evolvable
40
40
 
41
41
  private
42
42
 
43
- def find_mutations_count(individuals)
43
+ def find_mutations_count(objects)
44
44
  return 0 if @rate.zero?
45
45
 
46
- count = (individuals.count * evolvable_genes_count * @rate)
46
+ count = (objects.count * evolvable_genes_count * @rate)
47
47
  return count.to_i if count >= 1
48
48
 
49
49
  rand <= count ? 1 : 0
@@ -11,7 +11,7 @@ module Evolvable
11
11
  mutation: Mutation.new,
12
12
  generation_count: 0,
13
13
  log_progress: false,
14
- individuals: [])
14
+ objects: [])
15
15
  @evolvable_class = evolvable_class
16
16
  @size = size
17
17
  @selection_count = selection_count
@@ -19,7 +19,7 @@ module Evolvable
19
19
  @mutation = mutation
20
20
  @generation_count = generation_count
21
21
  @log_progress = log_progress
22
- assign_individuals(individuals)
22
+ assign_objects(objects)
23
23
  end
24
24
 
25
25
  attr_reader :evolvable_class,
@@ -28,7 +28,7 @@ module Evolvable
28
28
  :crossover,
29
29
  :mutation,
30
30
  :generation_count,
31
- :individuals
31
+ :objects
32
32
 
33
33
  def_delegators :@evolvable_class,
34
34
  :evolvable_evaluate!,
@@ -43,49 +43,53 @@ module Evolvable
43
43
  generations_count.times do
44
44
  @generation_count += 1
45
45
  evolvable_before_evolution(self)
46
- evaluate_individuals!
46
+ evaluate_objects!
47
47
  log_progress if @log_progress
48
48
  break if fitness_goal_met?
49
49
 
50
- select_individuals!
50
+ select_objects!
51
51
  evolvable_after_select(self)
52
- reproduce_individuals!
53
- mutate_individuals!
52
+ crossover_objects!
53
+ mutate_objects!
54
54
  evolvable_after_evolution(self)
55
55
  end
56
56
  end
57
57
 
58
- def evaluate_individuals!
59
- evolvable_evaluate!(@individuals)
58
+ def strongest_object
59
+ objects.max_by(&:fitness)
60
+ end
61
+
62
+ def evaluate_objects!
63
+ evolvable_evaluate!(@objects)
60
64
  if @fitness_goal
61
- @individuals.sort_by! { |i| -(i.fitness - @fitness_goal).abs }
65
+ @objects.sort_by! { |i| -(i.fitness - @fitness_goal).abs }
62
66
  else
63
- @individuals.sort_by!(&:fitness)
67
+ @objects.sort_by!(&:fitness)
64
68
  end
65
69
  end
66
70
 
67
71
  def log_progress
68
- @individuals.last.evolvable_progress
72
+ @objects.last.evolvable_progress
69
73
  end
70
74
 
71
75
  def fitness_goal_met?
72
- @fitness_goal && @individuals.last.fitness >= @fitness_goal
76
+ @fitness_goal && @objects.last.fitness >= @fitness_goal
73
77
  end
74
78
 
75
- def select_individuals!
76
- @individuals.slice!(0..-1 - @selection_count)
79
+ def select_objects!
80
+ @objects.slice!(0..-1 - @selection_count)
77
81
  end
78
82
 
79
- def reproduce_individuals!
80
- parent_genes = @individuals.map(&:genes)
83
+ def crossover_objects!
84
+ parent_genes = @objects.map(&:genes)
81
85
  offspring_genes = @crossover.call(parent_genes, @size)
82
- @individuals = offspring_genes.map.with_index do |genes, i|
86
+ @objects = offspring_genes.map.with_index do |genes, i|
83
87
  evolvable_initialize(genes, self, i)
84
88
  end
85
89
  end
86
90
 
87
- def mutate_individuals!
88
- @mutation.call!(@individuals)
91
+ def mutate_objects!
92
+ @mutation.call!(@objects)
89
93
  end
90
94
 
91
95
  def inspect
@@ -103,11 +107,11 @@ module Evolvable
103
107
 
104
108
  private
105
109
 
106
- def assign_individuals(individuals)
107
- @individuals = individuals || []
108
- (@size - individuals.count).times do |n|
110
+ def assign_objects(objects)
111
+ @objects = objects || []
112
+ (@size - objects.count).times do |n|
109
113
  genes = evolvable_random_genes
110
- @individuals << evolvable_initialize(genes, self, n)
114
+ @objects << evolvable_initialize(genes, self, n)
111
115
  end
112
116
  end
113
117
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Evolvable
4
- VERSION = '0.1.1'
4
+ VERSION = '0.1.2'
5
5
  end
data/lib/evolvable.rb CHANGED
@@ -8,14 +8,14 @@ require 'evolvable/population'
8
8
  require 'evolvable/crossover'
9
9
  require 'evolvable/mutation'
10
10
  require 'evolvable/helper_methods'
11
- require 'evolvable/callbacks'
11
+ require 'evolvable/hooks'
12
12
  require 'evolvable/errors/not_implemented'
13
13
 
14
14
  module Evolvable
15
15
  extend HelperMethods
16
16
 
17
17
  def self.included(base)
18
- base.extend Callbacks
18
+ base.extend Hooks
19
19
 
20
20
  def base.evolvable_gene_pool
21
21
  raise Errors::NotImplemented, __method__
@@ -25,14 +25,7 @@ module Evolvable
25
25
  evolvable_gene_pool_size
26
26
  end
27
27
 
28
- def base.evolvable_evaluate!(_individuals); end
29
-
30
- def base.evolvable_initialize(genes, population, _individual_index)
31
- evolvable = new
32
- evolvable.genes = genes
33
- evolvable.population = population
34
- evolvable
35
- end
28
+ def base.evolvable_evaluate!(_objects); end
36
29
 
37
30
  def base.evolvable_population_attrs
38
31
  {}
@@ -45,6 +38,13 @@ module Evolvable
45
38
  Population.new(args)
46
39
  end
47
40
 
41
+ def base.evolvable_initialize(genes, population, _object_index)
42
+ evolvable = new
43
+ evolvable.genes = genes
44
+ evolvable.population = population
45
+ evolvable
46
+ end
47
+
48
48
  def base.evolvable_gene_pool_cache
49
49
  @evolvable_gene_pool_cache ||= evolvable_gene_pool
50
50
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evolvable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Ruzicka
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-03-09 00:00:00.000000000 Z
11
+ date: 2019-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -68,10 +68,10 @@ files:
68
68
  - bin/setup
69
69
  - evolvable.gemspec
70
70
  - lib/evolvable.rb
71
- - lib/evolvable/callbacks.rb
72
71
  - lib/evolvable/crossover.rb
73
72
  - lib/evolvable/errors/not_implemented.rb
74
73
  - lib/evolvable/helper_methods.rb
74
+ - lib/evolvable/hooks.rb
75
75
  - lib/evolvable/mutation.rb
76
76
  - lib/evolvable/population.rb
77
77
  - lib/evolvable/version.rb
@@ -100,5 +100,5 @@ requirements: []
100
100
  rubygems_version: 3.0.3
101
101
  signing_key:
102
102
  specification_version: 4
103
- summary: Make Ruby objects evolvable with genetic algorithms
103
+ summary: Add evolutionary behavior to any Ruby object
104
104
  test_files: []