evolvable 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +272 -45
- data/bin/console +36 -0
- data/lib/evolvable/mutation.rb +2 -0
- data/lib/evolvable/population.rb +10 -9
- data/lib/evolvable/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 046213e8982bac4913f713e33942e9ef5ead7bd655871f995cbbe36797ce64f9
|
4
|
+
data.tar.gz: 2980cf144a9ed46e3abea9d0d395ee582a1207002fa2ab88bb2c967580f3bd6e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 934eb04d489153638b00c002a2315e35140867f36ebfd9ecd8b3eb30fc0b6f249991a7b5f7b8f83a848bc3de9c36d9e060a89e16e7492bdf7d59f18d63aff405
|
7
|
+
data.tar.gz: ed1cac7f1c24265c29488d2e58304fc81b57be1a2891b87ab4b229218da63f3961cc1ea9dfda926df7226b083db0f2262cbb343ec667f3aa603831887b70e0bd
|
data/README.md
CHANGED
@@ -1,24 +1,34 @@
|
|
1
1
|
# Evolvable
|
2
2
|
|
3
|
-
Genetic algorithms mimic biological processes such as natural selection, crossover, and mutation to model evolutionary behaviors in code.
|
3
|
+
Genetic algorithms mimic biological processes such as natural selection, crossover, and mutation to model evolutionary behaviors in code. This gem can add evolutionary behaviors to any Ruby object.
|
4
4
|
|
5
|
-
|
5
|
+
## Demos
|
6
6
|
|
7
|
-
-
|
8
|
-
-
|
9
|
-
- select_objects!
|
10
|
-
- crossover_objects!
|
11
|
-
- mutate_objects!
|
7
|
+
- [Evolvable Sentence](https://github.com/mattruzicka/evolvable_sentence) - An interactive version of the evolvable sentence example in "Getting Started"
|
8
|
+
- [Evolvable Equation](https://github.com/mattruzicka/evolvable_equation) - Evolves an equation of a specified length to evaluate to a given number
|
12
9
|
|
13
10
|
## Usage
|
14
11
|
|
15
|
-
|
12
|
+
- [Getting Started](#Getting-Started)
|
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)
|
22
|
+
|
23
|
+
### Getting Started
|
16
24
|
|
17
|
-
|
18
|
-
2. Define a class method named "evolvable_gene_pool"
|
19
|
-
3. Define an instance method named "fitness"
|
25
|
+
To build an object with evolvable behavior, do the following:
|
20
26
|
|
21
|
-
|
27
|
+
1. Add ```include Evolvable``` to your class
|
28
|
+
2. Define ```.evolvable_gene_pool``` ([documentation](#The-Gene-Pool))
|
29
|
+
3. Define ```#fitness``` ([documentation](#Fitness))
|
30
|
+
|
31
|
+
As an example, let's make a text-to-speech command evolve from saying random nonsense to whatever you desire. We'll start by defining a Sentence class and doing the three steps above:
|
22
32
|
|
23
33
|
```Ruby
|
24
34
|
require 'evolvable'
|
@@ -40,80 +50,295 @@ class Sentence
|
|
40
50
|
end
|
41
51
|
score
|
42
52
|
end
|
43
|
-
|
44
|
-
def words
|
45
|
-
@genes.values.join
|
46
|
-
end
|
47
53
|
end
|
48
54
|
```
|
49
55
|
|
50
56
|
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
57
|
|
52
58
|
```ruby
|
59
|
+
# To play with a more interactive version, check out https://github.com/mattruzicka/evolvable_sentence
|
60
|
+
|
53
61
|
population = Sentence.evolvable_population
|
54
62
|
object = population.strongest_object
|
55
63
|
|
56
64
|
until object.fitness == Sentence::DESIRED_WORDS.length
|
57
|
-
|
58
|
-
|
65
|
+
words = object.genes.values.join
|
66
|
+
puts words
|
67
|
+
system("say #{words}")
|
59
68
|
population.evolve!(fitness_goal: Sentence::DESIRED_WORDS.length)
|
60
69
|
object = population.strongest_object
|
61
70
|
end
|
71
|
+
|
72
|
+
```
|
73
|
+
|
74
|
+
The ```Evolvable::Population#evolve!``` method evolves the population from one generation to the next. It accepts two optional keyword arguments:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
{ generations_count: 1, fitness_goal: nil }
|
62
78
|
```
|
63
79
|
|
64
|
-
|
80
|
+
Specifying the **fitness_goal** is useful when there's a known fitness you're trying to achieve. We use it in the evolvable sentence example above. If you want your population to keep evolving until it hits a particular fitness goal, set **generations_count** to a large number. The **generations_count** keyword argument tells ```#evolve!``` how many times to run.
|
81
|
+
|
82
|
+
If you're interested in seeing exactly how ```Evolvable::Population#evolve!``` works, Open up the [Population](https://github.com/mattruzicka/evolvable/blob/master/lib/evolvable/population.rb) class and check out the following methods:
|
83
|
+
|
84
|
+
- ```evaluate_objects!```
|
85
|
+
- ```select_objects!```
|
86
|
+
- ```crossover_objects!```
|
87
|
+
- ```mutate_objects!```
|
65
88
|
|
66
89
|
### The Gene Pool
|
67
90
|
|
68
|
-
|
91
|
+
Currently, the gene pool needs to be an array of arrays. Each inner array contains a gene name and an array of possible values for the gene. Expect future releases to make defining the gene pool more straightforward. Check out [Development](#Development) below for details. Until then, here's an example of a simple ```.evolvable_gene_pool``` definition for evolvable dueling pianos:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
class DualingPianos
|
95
|
+
NOTES = ['C', 'C♯', 'D', 'D♯', 'E', 'F', 'F♯', 'G', 'G♯', 'A', 'A♯', 'B']
|
96
|
+
|
97
|
+
def self.evolvable_gene_pool
|
98
|
+
[[:piano_1, NOTES],
|
99
|
+
[:piano_2, NOTES]]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
The number of potential genes in the gene pool can be extremely large. For these cases, "Evolvable" makes it possible for objects to contain only a subset of genes with the ```.evolvable_genes_count``` method. Any gene defined in the ```.evolvable_gene_pool``` can still manifest during mutations. In this way, we can limit the gene size of particular objects without limiting the genes available to a population.
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
require 'evolvable'
|
108
|
+
|
109
|
+
class ComplexBot
|
110
|
+
include Evolvable
|
111
|
+
|
112
|
+
def self.evolvable_gene_pool
|
113
|
+
potential_gene_values = (1..10_000).to_a
|
114
|
+
Array.new(1_000_000) { |n| [n, potential_gene_values] }
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.evolvable_genes_count
|
118
|
+
5_000
|
119
|
+
end
|
120
|
+
end
|
69
121
|
|
70
|
-
|
71
|
-
|
122
|
+
ComplexBot.evolvable_gene_pool.size # => 1_000_000
|
123
|
+
population = ComplexBot.evolvable_population
|
124
|
+
complex_bot = population.objects.first
|
125
|
+
complex_bot.genes.count # => 10_000
|
126
|
+
```
|
72
127
|
|
73
128
|
### Fitness
|
74
129
|
|
75
|
-
|
130
|
+
The ```#fitness``` method is responsible for measuring how well an object performs. It returns a value or "score" which influences whether an object will be selected to "breed" the next generation of objects. How fitness is defined depends on what you're trying to achieve. If you're trying to break a high score record in Tetris, for example, then the fitness function for your tetris bot might simply return its tetris score:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
class TetrisBot
|
134
|
+
alias fitness tetris_score
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
138
|
+
If, however, your aim is to construct a bot that plays Tetris in an aesthetically pleasing way, maybe you'd define fitness by surveying your artist friends.
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
class TetrisArtBot
|
142
|
+
def fitness
|
143
|
+
artist_friend_ratings.sum / artist_friend_ratings.count
|
144
|
+
end
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
The result of ```#fitness``` can be any object that includes the ([Comparable](https://ruby-doc.org/core-2.6.1/Comparable.html)) mixin from Ruby and implements the ```<=>``` method. Many Ruby classes such as String and Integer have default implementations.
|
149
|
+
|
150
|
+
You may want to evaluate a whole generation of objects at once. For example, maybe you want each of your bots to play a game against each other and base your fitness score off their win records. For this case, use ```.evolvable_evaluate!(objects)``` like so:
|
151
|
+
|
152
|
+
```ruby
|
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
|
160
|
+
|
161
|
+
alias fitness win_count
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
### Evolvable Population
|
166
|
+
|
167
|
+
The Evolvable::Population class can be initialized in two ways
|
168
|
+
|
169
|
+
1. ```EvolvableBot.evolvable_population```
|
170
|
+
2. ```Evolvable::Population.new(evolvable_class: EvolvableBot)```
|
171
|
+
|
172
|
+
|
173
|
+
Both ways accept the following keyword arguments as defined by ```Evolvable::Population#initialize```
|
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
|
+
```
|
184
|
+
|
185
|
+
```.evolvable_population(args = {})``` merges any given args with with any keyword arguments defined in ```.evolvable_population_attrs``` Example:
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
class Plant
|
189
|
+
include Evolvable
|
190
|
+
|
191
|
+
def self.evolvable_population_attrs
|
192
|
+
{ size: 100,
|
193
|
+
selection_count: 5,
|
194
|
+
mutation: Evolvable::Mutation.new(rate: 0.3),
|
195
|
+
log_progress: false }
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.evolvable_gene_pool
|
199
|
+
[[:leaf_count, (1..100).to_a],
|
200
|
+
[:root_type, ['fibrous', 'taproot']]]
|
201
|
+
end
|
202
|
+
end
|
76
203
|
|
77
|
-
|
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
|
+
```
|
210
|
+
|
211
|
+
The ```.evolvable_initialize(genes, population, object_index)``` is used by Evolvable::Population to initialize new objects. You can override it to control how your objects are initialized. Make sure to assign the passed in **genes** to your initialized objects. Here's an example implementation for when you want your imaginary friends to have names:
|
78
212
|
|
79
|
-
|
213
|
+
```ruby
|
214
|
+
class ImaginaryFriend
|
215
|
+
def self.evolvable_initialize(genes, population, object_index)
|
216
|
+
friend = new(name: BABY_NAMES.sample)
|
217
|
+
friend.genes = genes
|
218
|
+
friend.population = population
|
219
|
+
friend
|
220
|
+
end
|
221
|
+
end
|
222
|
+
```
|
80
223
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
224
|
+
The third argument to ```.evolvable_initialize``` is the index of the object in the population before being evaluated. It is useful when you what to give your objects more utilitarian names:
|
225
|
+
|
226
|
+
```ruby
|
227
|
+
friend.name == "#{name} #{population.generation_count}.#{object_index}" # => "ImaginaryFriend 0.11"
|
228
|
+
```
|
229
|
+
|
230
|
+
A time when you'd want to use Evolvable::Population initializer instead of ```EvolvableBot.evolvable_population``` is when you're re-initializing a population. For example, maybe you want to continue evolving a population of chat bots that you had previously saved to a database:
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
population = load_chat_bot_population
|
234
|
+
Evolvable::Population.new(evolvable_class: population.evolvable_class,
|
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
|
244
|
+
|
245
|
+
```#evolvable_progress``` is used by ```Evolvable::Population#evolve!``` to log the progress of the "strongest object" in each generation. That is, the object with the best fitness score. It runs just after objects are evaluated and the ```Evolvable::Population#strongest_object``` can be determined. ```Evolvable::Population#log_progress``` must equal true in order for the result of the ```#evolvable_progress``` to be logged.
|
246
|
+
|
247
|
+
In the [evolvable sentence demo](https://github.com/mattruzicka/evolvable_sentence), ```evolvable_progress``` is implemented in order to output the strongest object's generation count, fitness score, and words. In this example, we also use the "say" text-to-speech command to pronounce the words.
|
248
|
+
|
249
|
+
```Ruby
|
250
|
+
class Sentence
|
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
|
256
|
+
end
|
257
|
+
population.log_progress = true
|
258
|
+
```
|
259
|
+
|
260
|
+
Hooks can also be used to monitor progress.
|
85
261
|
|
86
262
|
### Hooks
|
87
263
|
|
88
|
-
|
264
|
+
You can define any the following class method hooks on any Evolvable class. They run during the evolution of each generation in ```Evolvable::Population#evolve!```
|
265
|
+
|
266
|
+
```.evolvable_before_evolution(population)```
|
89
267
|
|
90
|
-
|
91
|
-
*.evolvable_after_select(population)*
|
92
|
-
*.evolvable_after_evolution(population)*
|
268
|
+
```.evolvable_after_select(population)```
|
93
269
|
|
94
|
-
|
270
|
+
```.evolvable_after_evolution(population)```
|
95
271
|
|
96
|
-
|
272
|
+
### Mutation Objects
|
97
273
|
|
98
|
-
|
274
|
+
The [Evolvable::Mutation](https://github.com/mattruzicka/evolvable/blob/master/lib/evolvable/mutation.rb) class defines the default mutation implementation with a default mutation rate of 0.03. It can be initialized with a custom mutation rate like so:
|
99
275
|
|
100
|
-
|
276
|
+
```ruby
|
277
|
+
mutation = Evolvable::Mutation.new(rate: 0.05)
|
278
|
+
population = Evolvable::Population.new(mutation: mutation)
|
279
|
+
population.evolve!
|
280
|
+
```
|
281
|
+
|
282
|
+
Any Ruby object that implements ```#call!(objects)``` can be used as a mutation object. The default implementation is specialized to work with evolvable objects that define a ```evolvable_genes_count``` that is less than ```evolvable_gene_pool.size```. For more information on this, see [The Gene Pool](#The-Gene-Pool)
|
283
|
+
|
284
|
+
### Crossover Objects
|
285
|
+
|
286
|
+
The [Evolvable::Crossover](https://github.com/mattruzicka/evolvable/blob/master/lib/evolvable/crossover.rb) class defines the default crossover implementation.
|
287
|
+
|
288
|
+
```ruby
|
289
|
+
crossover = Evolvable::Crossover.new
|
290
|
+
population = Evolvable::Population.new(crossover: crossover)
|
291
|
+
population.evolve!
|
292
|
+
```
|
293
|
+
|
294
|
+
Any Ruby object that implements ```#call(parent_genes, offspring_count)``` can be used as a crossover object. The default implementation is specialized to work with evolvable objects that define a ```evolvable_genes_count``` that is less than ```evolvable_gene_pool.size```. For more information on this, see [The Gene Pool](#The-Gene-Pool)
|
101
295
|
|
102
296
|
### Helper Methods
|
103
297
|
|
104
|
-
|
298
|
+
```Evolvable.combine_dimensions(dimensions)```
|
105
299
|
|
106
|
-
|
300
|
+
This is helpful when you want to create, for lack of a better word, "multidimensional genes". In the following example, we want our fortune cookie to evolve fortunes based on its eater's hair and eye color because fortune cookies understand things that we aren't capable of knowing.
|
107
301
|
|
108
|
-
|
302
|
+
```ruby
|
303
|
+
class FortuneCookie
|
304
|
+
include Evolvable
|
109
305
|
|
110
|
-
|
306
|
+
HAIR_COLORS = ['black', 'blond', 'brown', 'gray', 'red', 'white']
|
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']
|
111
312
|
|
112
|
-
|
313
|
+
def self.evolvable_gene_pool
|
314
|
+
gene_names_array = Evolvable.combine_dimensions([HAIR_COLORS, EYE_COLORS])
|
315
|
+
gene_names_array.map! { |gene_name| [gene_name, FORTUNES] }
|
316
|
+
end
|
113
317
|
|
114
|
-
|
115
|
-
|
116
|
-
|
318
|
+
def fitness
|
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
|
+
```
|
328
|
+
|
329
|
+
In this not-at-all-contrived example, ```Evolvable.combine_dimensions([HAIR_COLOR, EYE_COLORS])``` returns
|
330
|
+
|
331
|
+
```ruby
|
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
|
+
```
|
334
|
+
|
335
|
+
which is useful for composing genes made up of various dimensions and accessing gene values by these dimensions in the ```#fitness``` and ```.evolvable_evaluate!(objects)``` methods.
|
336
|
+
|
337
|
+
```Evolvable.combine_dimensions(dimensions)``` can take an array containing any number of arrays as an argument. One item from each given array will be in each output array and the item's index will be the same as the index of the argument array it belongs to. All combinations of items from the various arrays that follow this rule will be returned as arrays. The number of output arrays is equal to the product of multiplying the sizes of each given array. This method was difficult to write as was this description. I'd be really interested to see other people's implementations :)
|
338
|
+
|
339
|
+
### Configuration
|
340
|
+
|
341
|
+
TODO: Make logger configurable and make it smarter about picking a default
|
117
342
|
|
118
343
|
## Installation
|
119
344
|
|
@@ -141,6 +366,8 @@ I would also like to make more obvious how an evolvable object's genes can influ
|
|
141
366
|
|
142
367
|
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.
|
143
368
|
|
369
|
+
If you see a TODO in this README, feel free to do it :)
|
370
|
+
|
144
371
|
## Contributing
|
145
372
|
|
146
373
|
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/evolvable.
|
data/bin/console
CHANGED
@@ -10,5 +10,41 @@ require 'evolvable'
|
|
10
10
|
# require "pry"
|
11
11
|
# Pry.start
|
12
12
|
|
13
|
+
|
14
|
+
class BandMember
|
15
|
+
include Evolvable
|
16
|
+
|
17
|
+
SYNTHS = [:synth_1, :synth_2]
|
18
|
+
|
19
|
+
# SYNTH_OPTIONS = { synth_1: [[:cutoff, (1..100).to_a],
|
20
|
+
# [:reverb: (1..5).to_a]],
|
21
|
+
# synth_2: []}
|
22
|
+
|
23
|
+
SAMPLES = [:sample_1, :sample_2]
|
24
|
+
|
25
|
+
# SAMPLE_OPTIONS = { sample_1: [[:cutoff, (1..100).to_a],
|
26
|
+
# [:reverb: (1..5).to_a]],
|
27
|
+
# sample_2: []}
|
28
|
+
|
29
|
+
def self.evolvable_gene_pool
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def base.evolvable_random_genes(count = nil)
|
34
|
+
gene_pool = evolvable_gene_pool_cache
|
35
|
+
count ||= evolvable_genes_count
|
36
|
+
gene_pool = gene_pool.sample(count) if count < gene_pool.size
|
37
|
+
genes = {}
|
38
|
+
gene_pool.each { |name, potentials| genes[name] = potentials.sample }
|
39
|
+
genes
|
40
|
+
end
|
41
|
+
|
42
|
+
def fitness
|
43
|
+
0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
13
49
|
require 'irb'
|
14
50
|
IRB.start(__FILE__)
|
data/lib/evolvable/mutation.rb
CHANGED
data/lib/evolvable/population.rb
CHANGED
@@ -22,13 +22,14 @@ module Evolvable
|
|
22
22
|
assign_objects(objects)
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
25
|
+
attr_accessor :evolvable_class,
|
26
|
+
:size,
|
27
|
+
:selection_count,
|
28
|
+
:crossover,
|
29
|
+
:mutation,
|
30
|
+
:generation_count,
|
31
|
+
:log_progress,
|
32
|
+
:objects
|
32
33
|
|
33
34
|
def_delegators :@evolvable_class,
|
34
35
|
:evolvable_evaluate!,
|
@@ -44,7 +45,7 @@ module Evolvable
|
|
44
45
|
@generation_count += 1
|
45
46
|
evolvable_before_evolution(self)
|
46
47
|
evaluate_objects!
|
47
|
-
|
48
|
+
log_evolvable_progress if log_progress
|
48
49
|
break if fitness_goal_met?
|
49
50
|
|
50
51
|
select_objects!
|
@@ -68,7 +69,7 @@ module Evolvable
|
|
68
69
|
end
|
69
70
|
end
|
70
71
|
|
71
|
-
def
|
72
|
+
def log_evolvable_progress
|
72
73
|
@objects.last.evolvable_progress
|
73
74
|
end
|
74
75
|
|
data/lib/evolvable/version.rb
CHANGED
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.
|
4
|
+
version: 0.1.3
|
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-
|
11
|
+
date: 2019-03-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|