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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +4 -0
  3. data/CHANGELOG.md +56 -1
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +38 -21
  6. data/LICENSE +21 -0
  7. data/README.md +234 -161
  8. data/README_YARD.md +237 -0
  9. data/bin/console +18 -5
  10. data/evolvable.gemspec +2 -2
  11. data/examples/ascii_art.rb +62 -0
  12. data/examples/ascii_gene.rb +9 -0
  13. data/examples/hello_world.rb +91 -0
  14. data/examples/images/diagram.png +0 -0
  15. data/examples/stickman.rb +77 -0
  16. data/exe/hello +16 -0
  17. data/lib/evolvable/count_gene.rb +42 -0
  18. data/lib/evolvable/equalize_goal.rb +29 -0
  19. data/lib/evolvable/evaluation.rb +29 -6
  20. data/lib/evolvable/evolution.rb +40 -8
  21. data/lib/evolvable/gene.rb +54 -2
  22. data/lib/evolvable/gene_combination.rb +73 -0
  23. data/lib/evolvable/genome.rb +86 -0
  24. data/lib/evolvable/goal.rb +36 -3
  25. data/lib/evolvable/maximize_goal.rb +30 -0
  26. data/lib/evolvable/minimize_goal.rb +29 -0
  27. data/lib/evolvable/mutation.rb +66 -15
  28. data/lib/evolvable/point_crossover.rb +33 -19
  29. data/lib/evolvable/population.rb +171 -31
  30. data/lib/evolvable/rigid_count_gene.rb +17 -0
  31. data/lib/evolvable/search_space.rb +181 -0
  32. data/lib/evolvable/selection.rb +28 -1
  33. data/lib/evolvable/serializer.rb +21 -0
  34. data/lib/evolvable/uniform_crossover.rb +28 -8
  35. data/lib/evolvable/version.rb +1 -1
  36. data/lib/evolvable.rb +197 -29
  37. metadata +38 -27
  38. data/.rubocop.yml +0 -20
  39. data/examples/evolvable_string/char_gene.rb +0 -9
  40. data/examples/evolvable_string.rb +0 -32
  41. data/lib/evolvable/gene_crossover.rb +0 -28
  42. data/lib/evolvable/gene_space.rb +0 -37
  43. data/lib/evolvable/goal/equalize.rb +0 -19
  44. data/lib/evolvable/goal/maximize.rb +0 -19
  45. data/lib/evolvable/goal/minimize.rb +0 -19
data/README.md CHANGED
@@ -1,286 +1,359 @@
1
- # Evolvable
1
+ # Evolvable 🦎
2
2
 
3
- A framework for building evolutionary behaviors in Ruby.
3
+ [![Gem Version](https://badge.fury.io/rb/evolvable.svg)](https://badge.fury.io/rb/evolvable) [![Maintainability](https://api.codeclimate.com/v1/badges/7faf84a6d467718b33c0/maintainability)](https://codeclimate.com/github/mattruzicka/evolvable/maintainability)
4
4
 
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.
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
- With a straightforward and extensible API, Evolvable aims to make building simple as well as complex evolutionary algorithms fun and relatively easy.
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
- 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.
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 `gem 'evolvable'` to your application's Gemfile and run `bundle install` or install it yourself with `gem install evolvable`
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
- After installing and requiring the "evolvable" Ruby gem:
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
- 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
+ ### Implementation Steps
26
36
 
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.
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
- ## 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)
43
+ To demonstrate these steps, we'll look at the [Hello World](#) example program.
40
44
 
41
- ## Configuration
45
+ ### Hello World
42
46
 
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:
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
- ```ruby
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
- def value
54
- # Required
55
- end
56
- end
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
- The `Evolvable` module exposes the following class and instance methods for you.
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
- Here's a sample definition for the melody class defined above. It configures each instance to have 16 note genes and 1 instrument gene.
72
+ Let's begin by defining a `HelloWorld` class and have it **include the `Evolvable` module**.
66
73
 
67
74
  ```ruby
68
- def self.gene_space
69
- { instrument: { type: 'InstrumentGene', count: 1 },
70
- notes: { type: 'NoteGene', count: 16 } }
75
+ class HelloWorld
76
+ include Evolvable
71
77
  end
72
78
  ```
73
79
 
74
- See the section on [Genes](#genes) for more details.
75
-
76
- #### .new_population(keyword_args = {})
80
+ ### Step 2
77
81
 
78
- Initializes a new population. Example: `population = Melody.new_population(size: 100)`
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
- Accepts the same arguments as `Population.new`
84
+ ```ruby
85
+ class HelloWorld
86
+ include Evolvable
81
87
 
82
- #### .new_instance(population: nil, genes: [], population_index: nil)
88
+ def self.search_space
89
+ ["CharGene", 1..40]
90
+ end
91
+ end
92
+ ```
83
93
 
84
- Initializes a new instance. Accepts a population object, an array of gene objects, and the instance's population index.
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
- This method is useful for re-initializing instances and populations that have been saved.
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
- _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.
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
- #### .initialize_instance
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
- The default implementation simply delegates to `.new` and is useful for instances with custom initialize methods.
102
+ ```ruby
103
+ class CharGene
104
+ include Evolvable::Gene
93
105
 
94
- #### #initialize_instance
106
+ def self.chars
107
+ @chars ||= 32.upto(126).map(&:chr)
108
+ end
95
109
 
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.
110
+ def to_s
111
+ @to_s ||= self.class.chars.sample
112
+ end
113
+ end
114
+ ```
97
115
 
98
- #### #population, #population=
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
- The population object being used to evolve this instance.
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
- #### #genes, #genes=
120
+ ### Step 3
103
121
 
104
- An array of all an instance's genes. You can find specific types of genes with the following two methods.
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
- #### #find_genes(key)
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
- 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)`
126
+ ### Step 4
109
127
 
110
- #### #find_gene(key)
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
- 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)`
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
- #### #population_index, #population_index=
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
- 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.
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
- It is required that you implement this method. It is used when evaluating instances before undergoing evolution.
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
- 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.
143
+ ### Evolvable Population Hooks
123
144
 
124
- #### Evolvable Hooks
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
- 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:
147
+ 1. `.before_evaluation(population)` - Runs before evaluation.
127
148
 
128
- **.before_evaluation(population)**
149
+ 2. `.before_evolution(population)`- Runs after evaluation and before evolution.
129
150
 
130
- **.before_evolution(population)**
151
+ 3. `.after_evolution(population)` - Runs after evolution.
131
152
 
132
- **.after_evolution(population)**
133
153
 
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:
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 Melody
157
+ class HelloWorld
158
+ include Evolvable
159
+
138
160
  def self.before_evolution(population)
139
- best_melody = population.best_instance
140
- best_melody.play
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
- def play
144
- note_genes = melody.find_genes(:notes)
145
- note_values = note_genes.map(&:value)
146
- find_gene(:instrument).play(note_values)
166
+ # ...
167
+
168
+ def to_s
169
+ @to_s ||= genes.join
147
170
  end
171
+
172
+ # ...
148
173
  end
149
174
  ```
150
175
 
151
- ## Genes
176
+ Finally we can **evolve the population with the `Evolvable::Population#evolve` instance method**.
152
177
 
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.
178
+ ```ruby
179
+ population.evolve
180
+ ```
154
181
 
155
- #### The Evolvable::Gene module
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
- Gene objects must include the `Evolvable::Gene` module which enables them to undergo evolutionary operations such as crossover and mutation.
184
+ ## Concepts
158
185
 
159
- To continue with the melody example, we might encode a NoteGene like so:
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
- ```ruby
162
- class NoteGene
163
- include Evolvable::Gene
188
+ The following concept map depicts how genes flow through populations.
164
189
 
165
- NOTES = ['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']
190
+ ![Concept Map](https://github.com/mattruzicka/evolvable/raw/main/examples/images/diagram.png)
166
191
 
167
- def value
168
- @value ||= NOTES.sample
169
- end
170
- end
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.
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
- _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.
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
- class InstrumentGene
180
- include Evolvable::Gene
210
+ # This gene generates a random hexidecimal color code for use by evolvables.
181
211
 
182
- def instrument_class
183
- @instrument_class ||= [Guitar, Synth, Trumpet].sample
184
- end
212
+ require 'securerandom'
185
213
 
186
- def volume
187
- @volume ||= rand(1..100)
188
- end
214
+ class ColorGene
215
+ include Evolvable::Gene
189
216
 
190
- def play(notes)
191
- instrument_class.play(notes: notes, volume: volume)
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
- Now that its genes are implemented, a melody instance can use them:
224
+ [Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Gene)
199
225
 
200
- ```ruby
201
- class Melody
202
- include Evolvable
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
- 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
234
+ ```ruby
235
+ # TODO: initialize a population with all supported parameters
236
+ ```
215
237
 
216
- #### The Evolvable::GeneSpace object
217
238
 
218
- TODO
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
- TODO
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
- TODO
252
+ ```ruby
253
+ # TODO: Show how to add/change population's evaluation object
227
254
 
228
- #### The Evolvable::Goal::Maximize object
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
- #### The Evolvable::Goal::Minimize object
260
+ [Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Evaluation)
233
261
 
234
- TODO
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
- TODO
269
+ [Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Evolution)
239
270
 
240
- ## Populations
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
- TODO
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
- TODO
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
- #### The Evolvable::Evolution object
286
+ [Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Selection)
253
287
 
254
- TODO
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
- ## Selection
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
- #### The Evolvable::Selection object
297
+ [Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Combination)
261
298
 
262
- TODO
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
- ## Crossover
305
+ Mutation frequency is configurable using the `probability` and `rate`
306
+ parameters.
265
307
 
266
- TODO
267
308
 
268
- #### The Evolvable::GeneCrossover object
309
+ ```ruby
310
+ # Show how to initialize/assign population with a specific mutation object
311
+ ```
269
312
 
270
- TODO
271
313
 
272
- #### The Evolvable::UniformCrossover object
314
+ [Documentation](https://rubydoc.info/github/mattruzicka/evolvable/Evolvable/Mutation)
273
315
 
274
- TODO
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
- #### The Evolvable::PointCrossover object
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
- ## Mutation
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
- TODO
355
+ ## Contributing
283
356
 
284
- #### The Evolvable::Mutation object
357
+ Bug reports and pull requests are welcome on GitHub at https://github.com/mattruzicka/evolvable.
285
358
 
286
- TODO
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).