evolvable 0.1.1 → 0.1.2

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 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: []