evolvable 1.0.0 → 1.2.0
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 +4 -4
- data/.yardopts +4 -0
- data/CHANGELOG.md +56 -1
- data/Gemfile +3 -0
- data/Gemfile.lock +38 -21
- data/LICENSE +21 -0
- data/README.md +234 -161
- data/README_YARD.md +237 -0
- data/bin/console +18 -5
- data/evolvable.gemspec +2 -2
- data/examples/ascii_art.rb +62 -0
- data/examples/ascii_gene.rb +9 -0
- data/examples/hello_world.rb +91 -0
- data/examples/images/diagram.png +0 -0
- data/examples/stickman.rb +77 -0
- data/exe/hello +16 -0
- data/lib/evolvable/count_gene.rb +42 -0
- data/lib/evolvable/equalize_goal.rb +29 -0
- data/lib/evolvable/evaluation.rb +29 -6
- data/lib/evolvable/evolution.rb +40 -8
- data/lib/evolvable/gene.rb +54 -2
- data/lib/evolvable/gene_combination.rb +73 -0
- data/lib/evolvable/genome.rb +86 -0
- data/lib/evolvable/goal.rb +36 -3
- data/lib/evolvable/maximize_goal.rb +30 -0
- data/lib/evolvable/minimize_goal.rb +29 -0
- data/lib/evolvable/mutation.rb +66 -15
- data/lib/evolvable/point_crossover.rb +33 -19
- data/lib/evolvable/population.rb +171 -31
- data/lib/evolvable/rigid_count_gene.rb +17 -0
- data/lib/evolvable/search_space.rb +181 -0
- data/lib/evolvable/selection.rb +28 -1
- data/lib/evolvable/serializer.rb +21 -0
- data/lib/evolvable/uniform_crossover.rb +28 -8
- data/lib/evolvable/version.rb +1 -1
- data/lib/evolvable.rb +197 -29
- metadata +38 -27
- data/.rubocop.yml +0 -20
- data/examples/evolvable_string/char_gene.rb +0 -9
- data/examples/evolvable_string.rb +0 -32
- data/lib/evolvable/gene_crossover.rb +0 -28
- data/lib/evolvable/gene_space.rb +0 -37
- data/lib/evolvable/goal/equalize.rb +0 -19
- data/lib/evolvable/goal/maximize.rb +0 -19
- data/lib/evolvable/goal/minimize.rb +0 -19
data/README.md
CHANGED
@@ -1,286 +1,359 @@
|
|
1
|
-
# Evolvable
|
1
|
+
# Evolvable 🦎
|
2
2
|
|
3
|
-
|
3
|
+
[](https://badge.fury.io/rb/evolvable) [](https://codeclimate.com/github/mattruzicka/evolvable/maintainability)
|
4
4
|
|
5
|
-
|
5
|
+
An evolutionary framework for writing programs that use operations such as selection, crossover, and mutation. Explore ideas generatively in any domain, discover novel solutions to complex problems, and build intuitions about intelligence, complexity, and the natural world.
|
6
6
|
|
7
|
-
|
7
|
+
Subscribe to the [Evolvable Newsletter](https://www.evolvable.site/newsletter) to slowly learn more, or keep reading this contextualization of the [full documentation](https://rubydoc.info/github/mattruzicka/evolvable).
|
8
8
|
|
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
9
|
|
12
|
-
|
10
|
+
## Table of Contents
|
11
|
+
* [Installation](#installation)
|
12
|
+
* [Getting Started](#getting-started)
|
13
|
+
* [Concepts](#concepts)
|
14
|
+
* [Genes](#genes)
|
15
|
+
* [Populations](#populations)
|
16
|
+
* [Evaluation](#evaluation)
|
17
|
+
* [Evolution](#evolution)
|
18
|
+
* [Selection](#selection)
|
19
|
+
* [Combination](#combination)
|
20
|
+
* [Mutation](#mutation)
|
21
|
+
* [Search Space](#search-space)
|
22
|
+
|
13
23
|
|
14
24
|
## Installation
|
15
25
|
|
16
|
-
Add
|
26
|
+
Add [gem "evolvable"](https://rubygems.org/gems/evolvable) to your Gemfile and run `bundle install` or install it yourself with: `gem install evolvable`
|
17
27
|
|
18
28
|
## Getting Started
|
19
29
|
|
20
|
-
|
30
|
+
The `Evolvable` module makes it possible to implement evolutionary behaviors for
|
31
|
+
any class by defining a `.search_space` class method and `#value` instance method.
|
32
|
+
Then to evolve instances, initialize a population with `.new_population` and invoke
|
33
|
+
the `#evolve` method on the resulting population object.
|
21
34
|
|
22
|
-
|
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
|
+
### Implementation Steps
|
26
36
|
|
27
|
-
|
37
|
+
1. [Include the `Evolvable` module in the class you want to evolve.](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable)
|
38
|
+
2. [Define `.search_space` and any gene classes that you reference.](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/SearchSpace)
|
39
|
+
3. [Define `#value`.](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Evaluation)
|
40
|
+
4. [Initialize a population with `.new_population` and use `#evolve`.](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Population)
|
28
41
|
|
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.
|
30
42
|
|
31
|
-
|
32
|
-
- [Configuration](#Configuration)
|
33
|
-
- [Genes](#Genes)
|
34
|
-
- [Evaluation](#Evaluation)
|
35
|
-
- [Populations](#Populations)
|
36
|
-
- [Evolution](#Evolution)
|
37
|
-
- [Selection](#Selection)
|
38
|
-
- [Crossover](#Crossover)
|
39
|
-
- [Mutation](#Mutation)
|
43
|
+
To demonstrate these steps, we'll look at the [Hello World](#) example program.
|
40
44
|
|
41
|
-
|
45
|
+
### Hello World
|
42
46
|
|
43
|
-
|
47
|
+
Let's build the evolvable hello world program using the above steps. It'll evolve a population of arbitrary strings to be more like a given target string. After installing this gem, run `evolvable hello` at the command line to see it in action.
|
44
48
|
|
45
|
-
|
46
|
-
class Melody
|
47
|
-
include Evolvable
|
48
|
-
|
49
|
-
def self.gene_space
|
50
|
-
# Expected
|
51
|
-
end
|
49
|
+
Below is example output from evolving a population of randomly initialized string objects to match "Hello World!", then "Hello Evolvable World".
|
52
50
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
51
|
+
```
|
52
|
+
❯ Enter a string to evolve: Hello World!
|
53
|
+
|
54
|
+
pp`W^jXG'_N`% Generation 0
|
55
|
+
H-OQXZ\a~{H* Generation 1 ...
|
56
|
+
HRv9X WorlNi Generation 50 ...
|
57
|
+
HRl6W World# Generation 100 ...
|
58
|
+
Hello World! Generation 165
|
59
|
+
|
60
|
+
❯ Enter a string to evolve: Hello Evolvable World
|
61
|
+
|
62
|
+
Helgo World!b+=1}3 Generation 165
|
63
|
+
Helgo Worlv!}:c(SoV Generation 166
|
64
|
+
Helgo WorlvsC`X(Joqs Generation 167
|
65
|
+
Helgo WorlvsC`X(So1RE Generation 168 ...
|
66
|
+
Hello Evolv#"l{ Wor*5 Generation 300 ...
|
67
|
+
Hello Evolvable World Generation 388
|
57
68
|
```
|
58
69
|
|
59
|
-
|
60
|
-
|
61
|
-
#### .gene_space
|
62
|
-
|
63
|
-
You're expected to override this and return a gene space configuration hash or GeneSpace object.
|
70
|
+
### Step 1
|
64
71
|
|
65
|
-
|
72
|
+
Let's begin by defining a `HelloWorld` class and have it **include the `Evolvable` module**.
|
66
73
|
|
67
74
|
```ruby
|
68
|
-
|
69
|
-
|
70
|
-
notes: { type: 'NoteGene', count: 16 } }
|
75
|
+
class HelloWorld
|
76
|
+
include Evolvable
|
71
77
|
end
|
72
78
|
```
|
73
79
|
|
74
|
-
|
75
|
-
|
76
|
-
#### .new_population(keyword_args = {})
|
80
|
+
### Step 2
|
77
81
|
|
78
|
-
|
82
|
+
Now we can **define the `.search_space`** class method with the types of [genes](#genes) that we want our our evolvable "hello world" instances to be able to have. We'll use `CharGene` instances to represent single characters within strings. So an instance with the string value of "Hello" would be composed of five `CharGene` instances.
|
79
83
|
|
80
|
-
|
84
|
+
```ruby
|
85
|
+
class HelloWorld
|
86
|
+
include Evolvable
|
81
87
|
|
82
|
-
|
88
|
+
def self.search_space
|
89
|
+
["CharGene", 1..40]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
```
|
83
93
|
|
84
|
-
|
94
|
+
The [Search Space](#search-space) can be defined in a variety of ways. The above is shorthand that's useful for when there's only one type of gene. This method can also return an array of arrays or hash.
|
85
95
|
|
86
|
-
|
96
|
+
The `1..40` specifies the range of possible genes for a particular HelloWorld instance. Evolvable translates this range or integer value into a `Evolvable::CountGene` object.
|
87
97
|
|
88
|
-
|
98
|
+
By specifying a range, an `Evolvable::CountGene` instance can change the number of genes that are present in an evovlable instance. Count genes undergo evolutionary operations like any other gene. Their effects can be seen in the letter changes from Generation 165 to 168 in the above example output.
|
89
99
|
|
90
|
-
|
100
|
+
To finish step 2, we'll **define the gene class** that we referenced in the above `.search_space` method. Gene classes should include the `Evolvable::Gene` module.
|
91
101
|
|
92
|
-
|
102
|
+
```ruby
|
103
|
+
class CharGene
|
104
|
+
include Evolvable::Gene
|
93
105
|
|
94
|
-
|
106
|
+
def self.chars
|
107
|
+
@chars ||= 32.upto(126).map(&:chr)
|
108
|
+
end
|
95
109
|
|
96
|
-
|
110
|
+
def to_s
|
111
|
+
@to_s ||= self.class.chars.sample
|
112
|
+
end
|
113
|
+
end
|
114
|
+
```
|
97
115
|
|
98
|
-
|
116
|
+
It's important that, once accessed, the data for a particular gene never change. When the `#to_s` method first runs, Ruby's `||=` operator memoizes the result of randomly picking a char, enabling this method to sample a char only once per gene.
|
99
117
|
|
100
|
-
|
118
|
+
After defining the search space, we can now initialize `HelloWorld` instances with random genes, but to actually evolve them, we need to **define the `#value` instance method**. It provides the basis for comparing different evolvable instances.
|
101
119
|
|
102
|
-
|
120
|
+
### Step 3
|
103
121
|
|
104
|
-
|
122
|
+
In the next step, we'll set the goal value to 0, so that evolution favors evolvable HelloWorld instances with `#value` methods that return numbers closer to 0. That means we want instances that more closely match their targets to return scores nearer to 0. As an example, if our target is "hello world", an instance that returns "jello world" would have a value of 1 and "hello world" would have a value of 0.
|
105
123
|
|
106
|
-
|
124
|
+
For a working implementation, see the `#value` method in [examples/hello_world.rb](https://github.com/mattruzicka/evolvable/blob/main/examples/hello_world.rb)
|
107
125
|
|
108
|
-
|
126
|
+
### Step 4
|
109
127
|
|
110
|
-
|
128
|
+
Now it's time to **initialize a population with `.new_population`**. By default, evolvable populations seek to maximize numeric values. In this program, we always know the best possible value, so setting the goal to a concrete number makes sense. This is done by passing the evaluation params with equalize set to 0.
|
111
129
|
|
112
|
-
|
130
|
+
We'll also specify the number of instances in a population using the population's `size` parameter and change the mutation porbability from 0.03 (3%) to 0.6 (60%).
|
113
131
|
|
114
|
-
|
132
|
+
Experimentation has suggested that a large mutation probability tends to decrease the time it takes to evolve matches with short strings and has the opposite effect for long strings. This is demonstrated in the example output above by how many generations it took to go from "Hello World!" to "Hello Evolvable World". As an optimization, we could dynamically change the mutation probability using a population hook detailed below, but doing so will be left as an exercise for the reader. [Pull requests are welcome.](#contributing)
|
115
133
|
|
116
|
-
|
134
|
+
```ruby
|
135
|
+
population = HelloWorld.new_population(size: 100,
|
136
|
+
evaluation: { equalize: 0 },
|
137
|
+
mutation: { probability: 0.6 }
|
138
|
+
```
|
117
139
|
|
118
|
-
#### #value
|
119
140
|
|
120
|
-
|
141
|
+
At this point, everything should work when we run `population.evolve`, but it'll look like nothing is happening. The next section will allow us to gain instight by hooking into the evolutionary process.
|
121
142
|
|
122
|
-
|
143
|
+
### Evolvable Population Hooks
|
123
144
|
|
124
|
-
|
145
|
+
The following class methods can be implemented on your Evolvable class, e.g. HelloWorld, to hook into the Population#evolve lifecycle. This is useful for logging evolutionary progress, optimizing parameters, and creating interactions with and between evolvable instances.
|
125
146
|
|
126
|
-
|
147
|
+
1. `.before_evaluation(population)` - Runs before evaluation.
|
127
148
|
|
128
|
-
|
149
|
+
2. `.before_evolution(population)`- Runs after evaluation and before evolution.
|
129
150
|
|
130
|
-
|
151
|
+
3. `.after_evolution(population)` - Runs after evolution.
|
131
152
|
|
132
|
-
**.after_evolution(population)**
|
133
153
|
|
134
|
-
|
154
|
+
Let's define `.before_evolution` to print the best value for each generation. We'll also define `HelloWorld#to_s`, which implicitly delegates to `CharGene#to_s` during the string interpolation that happens.
|
135
155
|
|
136
156
|
```ruby
|
137
|
-
class
|
157
|
+
class HelloWorld
|
158
|
+
include Evolvable
|
159
|
+
|
138
160
|
def self.before_evolution(population)
|
139
|
-
|
140
|
-
|
161
|
+
best_evolvable = population.best_evolvable
|
162
|
+
evolutions_count = population.evolutions_count
|
163
|
+
puts "#{best_evolvable} - Generation #{evolutions_count}"
|
141
164
|
end
|
142
165
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
166
|
+
# ...
|
167
|
+
|
168
|
+
def to_s
|
169
|
+
@to_s ||= genes.join
|
147
170
|
end
|
171
|
+
|
172
|
+
# ...
|
148
173
|
end
|
149
174
|
```
|
150
175
|
|
151
|
-
|
176
|
+
Finally we can **evolve the population with the `Evolvable::Population#evolve` instance method**.
|
152
177
|
|
153
|
-
|
178
|
+
```ruby
|
179
|
+
population.evolve
|
180
|
+
```
|
154
181
|
|
155
|
-
|
182
|
+
**You now know the fundamental steps to building evolvable programs of endless complexity in any domain!** 🐸 The exact implementation for the command line demo can be found in [exe/hello](https://github.com/mattruzicka/evolvable/blob/main/exe/hello) and [examples/hello_world.rb](https://github.com/mattruzicka/evolvable/blob/main/examples/hello_world.rb).
|
156
183
|
|
157
|
-
|
184
|
+
## Concepts
|
158
185
|
|
159
|
-
|
186
|
+
[Populations](#populations) are composed of evolvables which are composed of genes. Evolvables orchestrate behaviors by delegating to gene objects. Collections of genes are organized into genomes and constitute the [search space](#search-space). [Evaluation](#evaluation) and [evolution](#evolution) objects are used to evolve populations. By default, evolution is composed of [selection](#selection), [combination](#combination), and [mutation](#mutation).
|
160
187
|
|
161
|
-
|
162
|
-
class NoteGene
|
163
|
-
include Evolvable::Gene
|
188
|
+
The following concept map depicts how genes flow through populations.
|
164
189
|
|
165
|
-
|
190
|
+

|
166
191
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
192
|
+
Evolvable is designed with extensibility in mind. Evolvable objects such as [evaluation](#evaluation), [evolution](#evolution), [selection](#selection), [combination](#combination), and [mutation](#mutation) can be extended and swapped, potentially in ways that alter the above graph.
|
193
|
+
|
194
|
+
## Genes
|
195
|
+
For evolution to be effective, an evolvable's genes must be able to influence
|
196
|
+
its behavior. Evolvables are composed of genes that can be used to run simple
|
197
|
+
functions or orchestrate complex interactions. The level of abstraction is up
|
198
|
+
to you.
|
173
199
|
|
174
|
-
|
200
|
+
Defining gene classes requires encapsulating some "sample space" and returning
|
201
|
+
a sample outcome when a gene attribute is accessed. For evolution to proceed
|
202
|
+
in a non-random way, the same sample outcome should be returned every time
|
203
|
+
a particular gene is accessed with a particular set of parameters.
|
204
|
+
Memoization is a useful technique for doing just this. The
|
205
|
+
[memo_wise](https://github.com/panorama-ed/memo_wise) gem may be useful for
|
206
|
+
more complex memoizations.
|
175
207
|
|
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:
|
177
208
|
|
178
209
|
```ruby
|
179
|
-
|
180
|
-
include Evolvable::Gene
|
210
|
+
# This gene generates a random hexidecimal color code for use by evolvables.
|
181
211
|
|
182
|
-
|
183
|
-
@instrument_class ||= [Guitar, Synth, Trumpet].sample
|
184
|
-
end
|
212
|
+
require 'securerandom'
|
185
213
|
|
186
|
-
|
187
|
-
|
188
|
-
end
|
214
|
+
class ColorGene
|
215
|
+
include Evolvable::Gene
|
189
216
|
|
190
|
-
def
|
191
|
-
|
217
|
+
def hex_code
|
218
|
+
@hex_code ||= SecureRandom.hex(3)
|
192
219
|
end
|
193
220
|
end
|
194
221
|
```
|
195
222
|
|
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.
|
197
223
|
|
198
|
-
|
224
|
+
[Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Gene)
|
199
225
|
|
200
|
-
|
201
|
-
|
202
|
-
|
226
|
+
## Populations
|
227
|
+
Population objects are responsible for generating and evolving instances.
|
228
|
+
They orchestrate all the other Evolvable objects to do so.
|
203
229
|
|
204
|
-
|
230
|
+
Populations can be initialized and re-initialized with a number of useful
|
231
|
+
parameters.
|
205
232
|
|
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
|
-
```
|
213
233
|
|
214
|
-
|
234
|
+
```ruby
|
235
|
+
# TODO: initialize a population with all supported parameters
|
236
|
+
```
|
215
237
|
|
216
|
-
#### The Evolvable::GeneSpace object
|
217
238
|
|
218
|
-
|
239
|
+
[Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Population)
|
219
240
|
|
220
241
|
## Evaluation
|
242
|
+
For selection to be effective in the context of evolution, there needs to be
|
243
|
+
a way to compare evolvables. In the genetic algorithm, this is often
|
244
|
+
referred to as the "fitness function".
|
221
245
|
|
222
|
-
|
246
|
+
The `Evolvable::Evaluation` object expects evolvable instances to define a `#value` method that
|
247
|
+
returns some numeric value. Values are used to evaluate instances relative to each
|
248
|
+
other and with regards to some goal. Out of the box, the goal can be set
|
249
|
+
to maximize, minimize, or equalize numeric values.
|
223
250
|
|
224
|
-
#### The Evolvable::Evaluation object
|
225
251
|
|
226
|
-
|
252
|
+
```ruby
|
253
|
+
# TODO: Show how to add/change population's evaluation object
|
227
254
|
|
228
|
-
|
255
|
+
# The goal value can also be assigned via as argument to `Evolvable::Population#evolve`
|
256
|
+
population.evolve(goal_value: 1000)
|
257
|
+
```
|
229
258
|
|
230
|
-
TODO
|
231
259
|
|
232
|
-
|
260
|
+
[Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Evaluation)
|
233
261
|
|
234
|
-
|
262
|
+
## Evolution
|
263
|
+
After a population's instances are evaluated, they undergo evolution.
|
264
|
+
The default evolution object is composed of selection,
|
265
|
+
crossover, and mutation objects and applies them as operations to
|
266
|
+
a population's evolvables in that order.
|
235
267
|
|
236
|
-
#### The Evolvable::Goal::Equalize object
|
237
268
|
|
238
|
-
|
269
|
+
[Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Evolution)
|
239
270
|
|
240
|
-
##
|
271
|
+
## Selection
|
272
|
+
The selection object assumes that a population's evolvables have already
|
273
|
+
been sorted by the evaluation object. It selects "parent" evolvables to
|
274
|
+
undergo combination and thereby produce the next generation of evolvables.
|
241
275
|
|
242
|
-
|
276
|
+
Only two evolvables are selected as parents for each generation by default.
|
277
|
+
The selection size is configurable.
|
243
278
|
|
244
|
-
#### The Evolvable::Population object
|
245
279
|
|
246
|
-
|
280
|
+
```ruby
|
281
|
+
# TODO: Show how to add/change population's selection object
|
282
|
+
```
|
247
283
|
|
248
|
-
## Evolution
|
249
284
|
|
250
|
-
TODO
|
251
285
|
|
252
|
-
|
286
|
+
[Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Selection)
|
253
287
|
|
254
|
-
|
288
|
+
## Combination
|
289
|
+
Combination generates new evolvable instances by combining the genes of selected instances.
|
290
|
+
You can think of it as a mixing of parent genes from one generation to
|
291
|
+
produce the next generation.
|
255
292
|
|
256
|
-
|
293
|
+
You may choose from a selection of combination objects or implement your own.
|
294
|
+
The default combination object is `Evolvable::GeneCombination`.
|
257
295
|
|
258
|
-
TODO
|
259
296
|
|
260
|
-
|
297
|
+
[Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Combination)
|
261
298
|
|
262
|
-
|
299
|
+
## Mutation
|
300
|
+
Mutation serves the role of increasing genetic variation. When an evolvable
|
301
|
+
undergoes a mutation, one or more of its genes are replaced by newly
|
302
|
+
initialized ones. In effect, a gene mutation invokes a new random outcome
|
303
|
+
from the genetic search space.
|
263
304
|
|
264
|
-
|
305
|
+
Mutation frequency is configurable using the `probability` and `rate`
|
306
|
+
parameters.
|
265
307
|
|
266
|
-
TODO
|
267
308
|
|
268
|
-
|
309
|
+
```ruby
|
310
|
+
# Show how to initialize/assign population with a specific mutation object
|
311
|
+
```
|
269
312
|
|
270
|
-
TODO
|
271
313
|
|
272
|
-
|
314
|
+
[Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Mutation)
|
273
315
|
|
274
|
-
|
316
|
+
## Search Space
|
317
|
+
The search space encapsulates the range of possible genes
|
318
|
+
for a particular evolvable. You can think of it as the boundaries of
|
319
|
+
genetic variation. It is configured via the
|
320
|
+
[.search_space](#evolvableclasssearch_space) method that you define
|
321
|
+
on your evolvable class. It's used by populations to initialize
|
322
|
+
new evolvables.
|
275
323
|
|
276
|
-
|
324
|
+
Evolvable provides flexibility in how you define your search space.
|
325
|
+
The below example implementations for `.search_space` produce the
|
326
|
+
exact same search space for the
|
327
|
+
[Hello World](https://github.com/mattruzicka/evolvable#hello-world)
|
328
|
+
demo program. The different styles arguably vary in suitability for
|
329
|
+
different contexts, perhaps depending on how programs are loaded and
|
330
|
+
the number of different gene types.
|
277
331
|
|
278
|
-
TODO
|
279
332
|
|
280
|
-
|
333
|
+
```ruby
|
334
|
+
# All 9 of these example definitions are equivalent
|
335
|
+
|
336
|
+
# Hash syntax
|
337
|
+
{ chars: { type: 'CharGene', max_count: 100 } }
|
338
|
+
{ chars: { type: 'CharGene', min_count: 1, max_count: 100 } }
|
339
|
+
{ chars: { type: 'CharGene', count: 1..100 } }
|
340
|
+
|
341
|
+
# Array of arrays syntax
|
342
|
+
[[:chars, 'CharGene', 1..100]]
|
343
|
+
[['chars', 'CharGene', 1..100]]
|
344
|
+
[['CharGene', 1..100]]
|
345
|
+
|
346
|
+
# A single array works when there's only one type of gene
|
347
|
+
['CharGene', 1..100]
|
348
|
+
[:chars, 'CharGene', 1..100]
|
349
|
+
['chars', 'CharGene', 1..100]
|
350
|
+
```
|
351
|
+
|
352
|
+
|
353
|
+
[Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/SearchSpace)
|
281
354
|
|
282
|
-
|
355
|
+
## Contributing
|
283
356
|
|
284
|
-
|
357
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/mattruzicka/evolvable.
|
285
358
|
|
286
|
-
|
359
|
+
If you're interested in contributing, but don't know where to get started, message me on twitter at [@mattruzicka](https://twitter.com/mattruzicka).
|