evolvable 0.1.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +9 -26
- data/README.md +167 -254
- data/bin/console +5 -41
- data/evolvable.gemspec +0 -1
- data/examples/evolvable_string.rb +32 -0
- data/examples/evolvable_string/char_gene.rb +9 -0
- data/lib/evolvable.rb +45 -58
- data/lib/evolvable/error/undefined_method.rb +7 -0
- data/lib/evolvable/evaluation.rb +51 -0
- data/lib/evolvable/evolution.rb +26 -0
- data/lib/evolvable/gene.rb +13 -0
- data/lib/evolvable/gene_crossover.rb +28 -0
- data/lib/evolvable/gene_space.rb +37 -0
- data/lib/evolvable/goal.rb +19 -0
- data/lib/evolvable/goal/equalize.rb +19 -0
- data/lib/evolvable/goal/maximize.rb +19 -0
- data/lib/evolvable/goal/minimize.rb +19 -0
- data/lib/evolvable/mutation.rb +22 -44
- data/lib/evolvable/point_crossover.rb +57 -0
- data/lib/evolvable/population.rb +70 -91
- data/lib/evolvable/selection.rb +18 -0
- data/lib/evolvable/uniform_crossover.rb +22 -0
- data/lib/evolvable/version.rb +1 -1
- metadata +18 -7
- data/lib/evolvable/crossover.rb +0 -35
- data/lib/evolvable/errors/not_implemented.rb +0 -5
- data/lib/evolvable/helper_methods.rb +0 -45
- data/lib/evolvable/hooks.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f1a067e6b2b71bbbfb1ddd936a14401262a6ec59dce36305500fcabebcde47e
|
4
|
+
data.tar.gz: 943d7a9efe2f50a387efac9f3f48115beacc0a9550d6ec14f0765f4a17d4e2a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64db467632fdfedbc7671229a80e8bad20ac8c5495ec731ec1111991bdbf50616e12091886c142fc44eaac5de1a5c4073e1c5f1ff8e5a304bc6272632e85411f
|
7
|
+
data.tar.gz: 3d8fd1aee8c638e521e6d5535cb3f00a5ca85064dec909790827dffad21ec9d9a13658b99d1ec0078e61d113356c2b950caefa84ffd86737f42f4ed8d71ee2a9
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Release 1.0.0
|
2
|
+
|
3
|
+
Hello 🌎 🌍 🌏
|
4
|
+
|
5
|
+
Check out the [README](https://github.com/mattruzicka/evolvable/blob/master/README.md) for everything
|
6
|
+
|
7
|
+
___
|
8
|
+
|
9
|
+
|
10
|
+
🧬 🧬 🧬
|
11
|
+
|
12
|
+
Please [open an issue](https://github.com/mattruzicka/evolvable/issues/new) if you cannot find what you're looking for
|
13
|
+
|
14
|
+
🧬 🧬 🧬
|
data/Gemfile.lock
CHANGED
@@ -1,34 +1,19 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
evolvable (
|
4
|
+
evolvable (1.0.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
ast (2.4.
|
10
|
-
byebug (11.
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
ast (~> 2.4.0)
|
9
|
+
ast (2.4.1)
|
10
|
+
byebug (11.1.3)
|
11
|
+
jaro_winkler (1.5.4)
|
12
|
+
parallel (1.19.2)
|
13
|
+
parser (2.7.1.4)
|
14
|
+
ast (~> 2.4.1)
|
16
15
|
powerpack (0.1.2)
|
17
16
|
rainbow (3.0.0)
|
18
|
-
rake (10.5.0)
|
19
|
-
rspec (3.8.0)
|
20
|
-
rspec-core (~> 3.8.0)
|
21
|
-
rspec-expectations (~> 3.8.0)
|
22
|
-
rspec-mocks (~> 3.8.0)
|
23
|
-
rspec-core (3.8.0)
|
24
|
-
rspec-support (~> 3.8.0)
|
25
|
-
rspec-expectations (3.8.2)
|
26
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
27
|
-
rspec-support (~> 3.8.0)
|
28
|
-
rspec-mocks (3.8.0)
|
29
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
-
rspec-support (~> 3.8.0)
|
31
|
-
rspec-support (3.8.0)
|
32
17
|
rubocop (0.64.0)
|
33
18
|
jaro_winkler (~> 1.5.1)
|
34
19
|
parallel (~> 1.10)
|
@@ -37,7 +22,7 @@ GEM
|
|
37
22
|
rainbow (>= 2.2.2, < 4.0)
|
38
23
|
ruby-progressbar (~> 1.7)
|
39
24
|
unicode-display_width (~> 1.4.0)
|
40
|
-
ruby-progressbar (1.10.
|
25
|
+
ruby-progressbar (1.10.1)
|
41
26
|
unicode-display_width (1.4.1)
|
42
27
|
|
43
28
|
PLATFORMS
|
@@ -47,9 +32,7 @@ DEPENDENCIES
|
|
47
32
|
bundler (~> 2.0)
|
48
33
|
byebug (~> 11.0)
|
49
34
|
evolvable!
|
50
|
-
rake (~> 10.0)
|
51
|
-
rspec (~> 3.0)
|
52
35
|
rubocop (~> 0.64.0)
|
53
36
|
|
54
37
|
BUNDLED WITH
|
55
|
-
2.
|
38
|
+
2.1.4
|
data/README.md
CHANGED
@@ -1,373 +1,286 @@
|
|
1
1
|
# Evolvable
|
2
2
|
|
3
|
-
|
3
|
+
A framework for building evolutionary behaviors in Ruby.
|
4
4
|
|
5
|
-
|
5
|
+
[Evolutionary algorithms](https://en.wikipedia.org/wiki/Evolutionary_algorithm) build upon ideas such as natural selection, crossover, and mutation to construct relatively simple solutions to complex problems. This gem has been used to implement evolutionary behaviors for [visual, textual, and auditory experiences](https://projectpag.es/evolvable) as well as a variety of AI agents.
|
6
6
|
|
7
|
-
|
8
|
-
- [Evolvable Equation](https://github.com/mattruzicka/evolvable_equation) - Evolves an equation of a specified length to evaluate to a given number
|
7
|
+
With a straightforward and extensible API, Evolvable aims to make building simple as well as complex evolutionary algorithms fun and relatively easy.
|
9
8
|
|
10
|
-
|
9
|
+
### The Evolvable Abstraction
|
10
|
+
Population objects are composed of instances that include the `Evolvable` module. Instances are composed of gene objects that include the `Evolvable::Gene` module. Evaluation and evolution objects are used by population objects to evolve your instances. An evaluation object has one goal object and the evolution object is composed of selection, crossover, and mutation objects by default.
|
11
11
|
|
12
|
-
|
13
|
-
- [The Gene Pool](#The-Gene-Pool)
|
14
|
-
- [Fitness](#Fitness)
|
15
|
-
- [Evolvable Population](#Evolvable-Population)
|
16
|
-
- [Monitoring Progress](#Monitoring-Progress)
|
17
|
-
- [Hooks](#Hooks)
|
18
|
-
- [Mutation Objects](#Mutation-Objects)
|
19
|
-
- [Crossover Objects](#Crossover-Objects)
|
20
|
-
- [Helper Methods](#Helper-Methods)
|
21
|
-
- [Configuration](#Configuration)
|
12
|
+
All classes exposed by Evolvable are prefixed with `Evolvable::` and can be configured, inherited, removed, and extended. You can also choose between various Evolvable implementations documented below or substitute your own. Don't worry if none of this made sense, this will become clearer as you continue reading.
|
22
13
|
|
23
|
-
|
14
|
+
## Installation
|
24
15
|
|
25
|
-
|
16
|
+
Add `gem 'evolvable'` to your application's Gemfile and run `bundle install` or install it yourself with `gem install evolvable`
|
26
17
|
|
27
|
-
|
28
|
-
2. Define ```.evolvable_gene_pool``` ([documentation](#The-Gene-Pool))
|
29
|
-
3. Define ```#fitness``` ([documentation](#Fitness))
|
18
|
+
## Getting Started
|
30
19
|
|
31
|
-
|
20
|
+
After installing and requiring the "evolvable" Ruby gem:
|
32
21
|
|
33
|
-
|
34
|
-
|
22
|
+
1. Include the `Evolvable` module in the class for the instances you want to evolve. (See [Configuration](#Configuration)).
|
23
|
+
2. Implement `.gene_space`, define any gene classes referenced by it, and include the `Evolvable::Gene` module for each. (See [Genes](#Genes)).
|
24
|
+
3. Implement `#value`. (See [Evaluation](#Evaluation)).
|
25
|
+
4. Initialize a population and start evolving. (See [Populations](#Populations)).
|
35
26
|
|
36
|
-
|
37
|
-
include Evolvable
|
27
|
+
Visit the [Evolvable Strings Tutorial](https://github.com/mattruzicka/evolvable/wiki/Evolvable-Strings-Tutorial) to see these steps in action. It walks through a simplified implementation of the [evolve string](https://github.com/mattruzicka/evolve_string) command-line program. Here's the [example source code](https://github.com/mattruzicka/evolvable/blob/master/examples/evolvable_string.rb) for the tutorial.
|
38
28
|
|
39
|
-
|
40
|
-
DESIRED_WORDS = 'colorless green ideas sleep furiously'
|
29
|
+
If you’d like to quickly play around with an evolvable string Population object, you can do so by cloning this repo and running the command `bin/console` in this project's directory.
|
41
30
|
|
42
|
-
|
43
|
-
|
31
|
+
## Usage
|
32
|
+
- [Configuration](#Configuration)
|
33
|
+
- [Genes](#Genes)
|
34
|
+
- [Evaluation](#Evaluation)
|
35
|
+
- [Populations](#Populations)
|
36
|
+
- [Evolution](#Evolution)
|
37
|
+
- [Selection](#Selection)
|
38
|
+
- [Crossover](#Crossover)
|
39
|
+
- [Mutation](#Mutation)
|
40
|
+
|
41
|
+
## Configuration
|
42
|
+
|
43
|
+
You'll need to define a class for the instances you want to evolve and include the `Evolvable` module. Let's say you want to evolve a melody. You might do something like this:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
class Melody
|
47
|
+
include Evolvable
|
48
|
+
|
49
|
+
def self.gene_space
|
50
|
+
# Expected
|
44
51
|
end
|
45
52
|
|
46
|
-
def
|
47
|
-
|
48
|
-
@genes.values.each_with_index do |value, index|
|
49
|
-
score += 1 if value == DESIRED_WORDS[index]
|
50
|
-
end
|
51
|
-
score
|
53
|
+
def value
|
54
|
+
# Required
|
52
55
|
end
|
53
56
|
end
|
54
57
|
```
|
55
58
|
|
56
|
-
|
59
|
+
The `Evolvable` module exposes the following class and instance methods for you.
|
57
60
|
|
58
|
-
|
59
|
-
# To play with a more interactive version, check out https://github.com/mattruzicka/evolvable_sentence
|
61
|
+
#### .gene_space
|
60
62
|
|
61
|
-
|
62
|
-
object = population.strongest_object
|
63
|
+
You're expected to override this and return a gene space configuration hash or GeneSpace object.
|
63
64
|
|
64
|
-
|
65
|
-
words = object.genes.values.join
|
66
|
-
puts words
|
67
|
-
system("say #{words}")
|
68
|
-
population.evolve!(fitness_goal: Sentence::DESIRED_WORDS.length)
|
69
|
-
object = population.strongest_object
|
70
|
-
end
|
65
|
+
Here's a sample definition for the melody class defined above. It configures each instance to have 16 note genes and 1 instrument gene.
|
71
66
|
|
67
|
+
```ruby
|
68
|
+
def self.gene_space
|
69
|
+
{ instrument: { type: 'InstrumentGene', count: 1 },
|
70
|
+
notes: { type: 'NoteGene', count: 16 } }
|
71
|
+
end
|
72
72
|
```
|
73
73
|
|
74
|
-
|
74
|
+
See the section on [Genes](#genes) for more details.
|
75
75
|
|
76
|
-
|
77
|
-
{ generations_count: 1, fitness_goal: nil }
|
78
|
-
```
|
76
|
+
#### .new_population(keyword_args = {})
|
79
77
|
|
80
|
-
|
78
|
+
Initializes a new population. Example: `population = Melody.new_population(size: 100)`
|
81
79
|
|
82
|
-
|
80
|
+
Accepts the same arguments as `Population.new`
|
83
81
|
|
84
|
-
|
85
|
-
- ```select_objects!```
|
86
|
-
- ```crossover_objects!```
|
87
|
-
- ```mutate_objects!```
|
82
|
+
#### .new_instance(population: nil, genes: [], population_index: nil)
|
88
83
|
|
89
|
-
|
84
|
+
Initializes a new instance. Accepts a population object, an array of gene objects, and the instance's population index.
|
90
85
|
|
91
|
-
|
86
|
+
This method is useful for re-initializing instances and populations that have been saved.
|
92
87
|
|
93
|
-
|
94
|
-
class DualingPianos
|
95
|
-
NOTES = ['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']
|
88
|
+
_It is not recommended that you override this method_ as it is used by Evolvable internals. If you need to customize how your instances are initialized you can override either of the following two "initialize_instance" methods.
|
96
89
|
|
97
|
-
|
98
|
-
[[:piano_1, NOTES],
|
99
|
-
[:piano_2, NOTES]]
|
100
|
-
end
|
101
|
-
end
|
102
|
-
```
|
90
|
+
#### .initialize_instance
|
103
91
|
|
104
|
-
The
|
92
|
+
The default implementation simply delegates to `.new` and is useful for instances with custom initialize methods.
|
105
93
|
|
106
|
-
|
107
|
-
require 'evolvable'
|
94
|
+
#### #initialize_instance
|
108
95
|
|
109
|
-
|
110
|
-
include Evolvable
|
96
|
+
Runs after Evolvable finishes building your instance. It's useful for stuff like implementing custom gene initialization logic. For example, the Evolvable Strings web demo (coming soon) uses it to read from a "length gene" and add or remove "char genes" accordingly.
|
111
97
|
|
112
|
-
|
113
|
-
potential_gene_values = (1..10_000).to_a
|
114
|
-
Array.new(1_000_000) { |n| [n, potential_gene_values] }
|
115
|
-
end
|
98
|
+
#### #population, #population=
|
116
99
|
|
117
|
-
|
118
|
-
5_000
|
119
|
-
end
|
120
|
-
end
|
100
|
+
The population object being used to evolve this instance.
|
121
101
|
|
122
|
-
|
123
|
-
population = ComplexBot.evolvable_population
|
124
|
-
complex_bot = population.objects.first
|
125
|
-
complex_bot.genes.count # => 10_000
|
126
|
-
```
|
102
|
+
#### #genes, #genes=
|
127
103
|
|
128
|
-
|
104
|
+
An array of all an instance's genes. You can find specific types of genes with the following two methods.
|
129
105
|
|
130
|
-
|
106
|
+
#### #find_genes(key)
|
131
107
|
|
132
|
-
|
133
|
-
class TetrisBot
|
134
|
-
alias fitness tetris_score
|
135
|
-
end
|
136
|
-
```
|
108
|
+
Returns an array of genes that have the given key. Gene keys are defined in the [.gene_space](####.gene_space) method. In the Melody example above, the key for the note genes would be `:notes`. The following would return an array of them: `note_genes = melody.find_genes(:notes)`
|
137
109
|
|
138
|
-
|
110
|
+
#### #find_gene(key)
|
139
111
|
|
140
|
-
|
141
|
-
class TetrisArtBot
|
142
|
-
def fitness
|
143
|
-
artist_friend_ratings.sum / artist_friend_ratings.count
|
144
|
-
end
|
145
|
-
end
|
146
|
-
```
|
112
|
+
Returns the first gene with the given key. In the Melody example above, the instrument gene has the key `:instrument` so we might write something like: `instrument_gene = melody.find_gene(instrument)`
|
147
113
|
|
148
|
-
|
114
|
+
#### #population_index, #population_index=
|
149
115
|
|
150
|
-
|
116
|
+
Returns an instance's population index - an integer representing the order in which it was initialized in a population. It's the most basic way to distinguish instances in a population.
|
151
117
|
|
152
|
-
|
153
|
-
class GamerBot
|
154
|
-
def self.evolvable_evaluate!(objects)
|
155
|
-
objects.combination(2).each do |player_1, player_2|
|
156
|
-
winner = Game.play(player_1, player_2)
|
157
|
-
winner.win_count += 1
|
158
|
-
end
|
159
|
-
end
|
118
|
+
#### #value
|
160
119
|
|
161
|
-
|
162
|
-
end
|
163
|
-
```
|
120
|
+
It is required that you implement this method. It is used when evaluating instances before undergoing evolution.
|
164
121
|
|
165
|
-
|
122
|
+
Technically, this method can return any object that implements Ruby's [Comparable](https://ruby-doc.org/core-2.7.1/Comparable.html). See the section on [Evaluation](#Evaluation) for details.
|
166
123
|
|
167
|
-
|
124
|
+
#### Evolvable Hooks
|
168
125
|
|
169
|
-
|
170
|
-
2. ```Evolvable::Population.new(evolvable_class: EvolvableBot)```
|
126
|
+
The following class methods can be overridden in order to hook into a population's evolutions. The hooks run for each evolution in the following order:
|
171
127
|
|
128
|
+
**.before_evaluation(population)**
|
172
129
|
|
173
|
-
|
174
|
-
```ruby
|
175
|
-
{ evolvable_class:, # Required. Inferred if you use the .evolvable_population method
|
176
|
-
size: 20, # The number of objects in each generation
|
177
|
-
selection_count: 2, # The number of objects to be selected as parents for crossover
|
178
|
-
crossover: Crossover.new, # Any object that implements #call
|
179
|
-
mutation: Mutation.new, # Any object that implements #call!
|
180
|
-
generation_count: 0, # Useful when you want to re-initialize a previously saved population of objects
|
181
|
-
objects: [], # Ditto
|
182
|
-
log_progress: false } # See the "Monitoring Progress" section for details
|
183
|
-
```
|
130
|
+
**.before_evolution(population)**
|
184
131
|
|
185
|
-
|
132
|
+
**.after_evolution(population)**
|
186
133
|
|
187
|
-
|
188
|
-
class Plant
|
189
|
-
include Evolvable
|
134
|
+
To use our Melody example from above, you could override the `.before_evolution` method to play the best melody from each generation with something like this:
|
190
135
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
136
|
+
```ruby
|
137
|
+
class Melody
|
138
|
+
def self.before_evolution(population)
|
139
|
+
best_melody = population.best_instance
|
140
|
+
best_melody.play
|
196
141
|
end
|
197
142
|
|
198
|
-
def
|
199
|
-
|
200
|
-
|
143
|
+
def play
|
144
|
+
note_genes = melody.find_genes(:notes)
|
145
|
+
note_values = note_genes.map(&:value)
|
146
|
+
find_gene(:instrument).play(note_values)
|
201
147
|
end
|
202
148
|
end
|
203
|
-
|
204
|
-
population = Plant.evolvable_population(log_progress: true)
|
205
|
-
population.size # => 100
|
206
|
-
population.selection_count # => 5
|
207
|
-
population.mutation.rate # => 0.3
|
208
|
-
population.log_progress # => true
|
209
149
|
```
|
210
150
|
|
211
|
-
|
151
|
+
## Genes
|
152
|
+
|
153
|
+
Instances rely on gene objects to compose behaviors. In other words, a gene can be thought of as an object that in some way affects the behavior of an instance. They are used to encapsulate a "sample space" and return a sample outcome when accessed.
|
154
|
+
|
155
|
+
#### The Evolvable::Gene module
|
156
|
+
|
157
|
+
Gene objects must include the `Evolvable::Gene` module which enables them to undergo evolutionary operations such as crossover and mutation.
|
158
|
+
|
159
|
+
To continue with the melody example, we might encode a NoteGene like so:
|
212
160
|
|
213
161
|
```ruby
|
214
|
-
class
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
162
|
+
class NoteGene
|
163
|
+
include Evolvable::Gene
|
164
|
+
|
165
|
+
NOTES = ['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']
|
166
|
+
|
167
|
+
def value
|
168
|
+
@value ||= NOTES.sample
|
220
169
|
end
|
221
170
|
end
|
222
171
|
```
|
172
|
+
Here, the "sample space" for the NoteGene class has twelve notes, but each individual object will have only one note. The note is randomly chosen when the "value" method is invoked for the first time.
|
223
173
|
|
224
|
-
|
225
|
-
|
226
|
-
```ruby
|
227
|
-
friend.name == "#{name} #{population.generation_count}.#{object_index}" # => "ImaginaryFriend 0.11"
|
228
|
-
```
|
174
|
+
_It is important that the data for a particular gene never change._ Ruby's or-equals operator `||=` is super useful for memoizing gene attributes like this. It is used above to randomly pick a note only once and return the same note for the lifetime of the object.
|
229
175
|
|
230
|
-
A
|
176
|
+
A melody instance with multiple note genes might use the `NoteGene#value` method to compose the notes of its melody like so: `melody.find_genes(:note).map(&:value)`. Let's keep going with this example and implement the `InstrumentGene` too:
|
231
177
|
|
232
178
|
```ruby
|
233
|
-
|
234
|
-
Evolvable::
|
235
|
-
size: population.size,
|
236
|
-
selection_count: population.selection_count,
|
237
|
-
crossover: population.crossover,
|
238
|
-
mutation: population.mutation,
|
239
|
-
generation_count: population.generation_count,
|
240
|
-
objects: population.objects)
|
241
|
-
```
|
242
|
-
|
243
|
-
### Monitoring Progress
|
179
|
+
class InstrumentGene
|
180
|
+
include Evolvable::Gene
|
244
181
|
|
245
|
-
|
182
|
+
def instrument_class
|
183
|
+
@instrument_class ||= [Guitar, Synth, Trumpet].sample
|
184
|
+
end
|
246
185
|
|
247
|
-
|
186
|
+
def volume
|
187
|
+
@volume ||= rand(1..100)
|
188
|
+
end
|
248
189
|
|
249
|
-
|
250
|
-
|
251
|
-
def evolvable_progress
|
252
|
-
words = @genes.values.join
|
253
|
-
puts "Generation: #{population.generation_count} | Fitness: #{fitness} | #{words}"
|
254
|
-
system("say #{words}") if say?
|
255
|
-
end
|
190
|
+
def play(notes)
|
191
|
+
instrument_class.play(notes: notes, volume: volume)
|
256
192
|
end
|
257
|
-
|
193
|
+
end
|
258
194
|
```
|
259
195
|
|
260
|
-
|
196
|
+
You can model your sample space however you like. Ruby's [Array](https://ruby-doc.org/core-2.7.1/Array.html), [Hash](https://ruby-doc.org/core-2.7.1/Hash.html), [Range](https://ruby-doc.org/core-2.7.1/Range.html), and [Random](https://ruby-doc.org/core-2.7.1/Random.html) classes may be useful. This InstrumentGene implementation has 300 possible outcomes (3 instruments * 100 volumes) and uses Ruby's Array, Range, and Random classes.
|
261
197
|
|
262
|
-
|
198
|
+
Now that its genes are implemented, a melody instance can use them:
|
263
199
|
|
264
|
-
|
200
|
+
```ruby
|
201
|
+
class Melody
|
202
|
+
include Evolvable
|
265
203
|
|
266
|
-
|
204
|
+
# ...
|
267
205
|
|
268
|
-
|
206
|
+
def play
|
207
|
+
note_genes = melody.find_genes(:notes)
|
208
|
+
note_values = note_genes.map(&:value)
|
209
|
+
find_gene(:instrument).play(note_values)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
```
|
269
213
|
|
270
|
-
|
214
|
+
In this way, instances can express behaviors via genes and even orchestrate interactions between them. Genes can also interact with each other during an instance's initialization process via the [#initialize_instance](#initialize_instance-1) method
|
271
215
|
|
272
|
-
|
216
|
+
#### The Evolvable::GeneSpace object
|
273
217
|
|
274
|
-
|
218
|
+
TODO
|
275
219
|
|
276
|
-
|
277
|
-
mutation = Evolvable::Mutation.new(rate: 0.05)
|
278
|
-
population = Evolvable::Population.new(mutation: mutation)
|
279
|
-
population.evolve!
|
280
|
-
```
|
220
|
+
## Evaluation
|
281
221
|
|
282
|
-
|
222
|
+
TODO
|
283
223
|
|
284
|
-
|
224
|
+
#### The Evolvable::Evaluation object
|
285
225
|
|
286
|
-
|
226
|
+
TODO
|
287
227
|
|
288
|
-
|
289
|
-
crossover = Evolvable::Crossover.new
|
290
|
-
population = Evolvable::Population.new(crossover: crossover)
|
291
|
-
population.evolve!
|
292
|
-
```
|
228
|
+
#### The Evolvable::Goal::Maximize object
|
293
229
|
|
294
|
-
|
230
|
+
TODO
|
295
231
|
|
296
|
-
|
232
|
+
#### The Evolvable::Goal::Minimize object
|
297
233
|
|
298
|
-
|
234
|
+
TODO
|
299
235
|
|
300
|
-
|
236
|
+
#### The Evolvable::Goal::Equalize object
|
301
237
|
|
302
|
-
|
303
|
-
class FortuneCookie
|
304
|
-
include Evolvable
|
238
|
+
TODO
|
305
239
|
|
306
|
-
|
307
|
-
EYE_COLORS = ['blue', 'brown', 'gray', 'green']
|
308
|
-
|
309
|
-
FORTUNES = ['You will prosper',
|
310
|
-
'You will endure hardship',
|
311
|
-
'You are about to eat a crisp and sugary cookie']
|
240
|
+
## Populations
|
312
241
|
|
313
|
-
|
314
|
-
gene_names_array = Evolvable.combine_dimensions([HAIR_COLORS, EYE_COLORS])
|
315
|
-
gene_names_array.map! { |gene_name| [gene_name, FORTUNES] }
|
316
|
-
end
|
242
|
+
TODO
|
317
243
|
|
318
|
-
|
319
|
-
hair_color = find_eater_hair_color
|
320
|
-
eye_color = find_eater_eye_color
|
321
|
-
fortune = @genes[[hair_color, eye_color]]
|
322
|
-
eater.give_fortune(fortune)
|
323
|
-
sleep 2.weeks
|
324
|
-
eater.how_true?(fortune)
|
325
|
-
end
|
326
|
-
end
|
327
|
-
```
|
244
|
+
#### The Evolvable::Population object
|
328
245
|
|
329
|
-
|
246
|
+
TODO
|
330
247
|
|
331
|
-
|
332
|
-
[["auburn", "blue"], ["auburn", "brown"], ["auburn", "gray"], ["auburn", "green"], ["black", "blue"], ["black", "brown"], ["black", "gray"], ["black", "green"], ["blond", "blue"], ["blond", "brown"], ["blond", "gray"], ["blond", "green"], ["brown", "blue"], ["brown", "brown"], ["brown", "gray"], ["brown", "green"], ["gray", "blue"], ["gray", "brown"], ["gray", "gray"], ["gray", "green"], ["red", "blue"], ["red", "brown"], ["red", "gray"], ["red", "green"], ["white", "blue"], ["white", "brown"], ["white", "gray"], ["white", "green"]]
|
333
|
-
```
|
248
|
+
## Evolution
|
334
249
|
|
335
|
-
|
250
|
+
TODO
|
336
251
|
|
337
|
-
|
252
|
+
#### The Evolvable::Evolution object
|
338
253
|
|
339
|
-
|
254
|
+
TODO
|
340
255
|
|
341
|
-
|
256
|
+
## Selection
|
342
257
|
|
343
|
-
|
258
|
+
TODO
|
344
259
|
|
345
|
-
|
260
|
+
#### The Evolvable::Selection object
|
346
261
|
|
347
|
-
|
348
|
-
gem 'evolvable'
|
349
|
-
```
|
262
|
+
TODO
|
350
263
|
|
351
|
-
|
264
|
+
## Crossover
|
352
265
|
|
353
|
-
|
266
|
+
TODO
|
354
267
|
|
355
|
-
|
268
|
+
#### The Evolvable::GeneCrossover object
|
356
269
|
|
357
|
-
|
270
|
+
TODO
|
358
271
|
|
359
|
-
|
272
|
+
#### The Evolvable::UniformCrossover object
|
360
273
|
|
361
|
-
|
274
|
+
TODO
|
362
275
|
|
363
|
-
|
276
|
+
#### The Evolvable::PointCrossover object
|
364
277
|
|
365
|
-
|
278
|
+
TODO
|
366
279
|
|
367
|
-
|
280
|
+
## Mutation
|
368
281
|
|
369
|
-
|
282
|
+
TODO
|
370
283
|
|
371
|
-
|
284
|
+
#### The Evolvable::Mutation object
|
372
285
|
|
373
|
-
|
286
|
+
TODO
|