rubyneat 0.3.5.alpha.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.directory +4 -0
- data/.gitignore.orig +20 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/dictionaries/trader.xml +3 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/misc.xml +5 -0
- data/.idea/modules.xml +9 -0
- data/.idea/rubyneat.iml +197 -0
- data/.idea/runConfigurations/invpend_neat.xml +26 -0
- data/.idea/runConfigurations/sigdebug_neat.xml +24 -0
- data/.idea/runConfigurations/xor_neat.xml +26 -0
- data/.idea/runConfigurations/xordebug_neat.xml +24 -0
- data/.idea/runConfigurations/xorsin_neat.xml +24 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/vcs.xml +7 -0
- data/.idea/workspace.xml +1124 -0
- data/.semver +5 -0
- data/.yardoc/checksums +11 -0
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/.yardoc/proxy_types +0 -0
- data/Gemfile +32 -0
- data/Gemfile.lock +135 -0
- data/Gemfile.lock.orig +147 -0
- data/Guardfile +8 -0
- data/Rakefile +61 -0
- data/bin/neat +83 -0
- data/config/application.rb +5 -0
- data/doc/ControllerPoint.html +125 -0
- data/doc/CuteA.html +286 -0
- data/doc/CuteB.html +297 -0
- data/doc/DSL.html +883 -0
- data/doc/NEAT/BasicNeuronTypes/BiasNeuron.html +518 -0
- data/doc/NEAT/BasicNeuronTypes/CosineNeuron.html +274 -0
- data/doc/NEAT/BasicNeuronTypes/InputNeuron.html +366 -0
- data/doc/NEAT/BasicNeuronTypes/SigmoidNeuron.html +275 -0
- data/doc/NEAT/BasicNeuronTypes/SineNeuron.html +274 -0
- data/doc/NEAT/BasicNeuronTypes/TanhNeuron.html +274 -0
- data/doc/NEAT/BasicNeuronTypes.html +136 -0
- data/doc/NEAT/Controller/NeatSettings.html +3985 -0
- data/doc/NEAT/Controller.html +2490 -0
- data/doc/NEAT/Critter/Genotype/Gene.html +979 -0
- data/doc/NEAT/Critter/Genotype.html +1601 -0
- data/doc/NEAT/Critter/Phenotype.html +603 -0
- data/doc/NEAT/Critter.html +1037 -0
- data/doc/NEAT/DSL.html +1255 -0
- data/doc/NEAT/Evaluator.html +420 -0
- data/doc/NEAT/Evolver/CritterOp.html +551 -0
- data/doc/NEAT/Evolver.html +602 -0
- data/doc/NEAT/Expressor.html +327 -0
- data/doc/NEAT/Graph/DependencyResolver.html +478 -0
- data/doc/NEAT/Graph/GraphException.html +123 -0
- data/doc/NEAT/Graph.html +402 -0
- data/doc/NEAT/NeatException.html +123 -0
- data/doc/NEAT/NeatOb.html +567 -0
- data/doc/NEAT/Neuron.html +1067 -0
- data/doc/NEAT/Operator.html +162 -0
- data/doc/NEAT/Population.html +1961 -0
- data/doc/NEAT/Trait.html +169 -0
- data/doc/NEAT.html +588 -0
- data/doc/_index.html +373 -0
- data/doc/class_list.html +54 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +339 -0
- data/doc/file_list.html +53 -0
- data/doc/frames.html +26 -0
- data/doc/index.html +373 -0
- data/doc/js/app.js +219 -0
- data/doc/js/full_list.js +178 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +1415 -0
- data/doc/top-level-namespace.html +164 -0
- data/foo/foo_aquarium_example.rb +38 -0
- data/foo/foo_gosu.rb +99 -0
- data/foo/foo_rubygoo.rb +104 -0
- data/foo/foo_sdl.rb +34 -0
- data/foo/icon.png +0 -0
- data/lib/rubyneat/critter.rb +374 -0
- data/lib/rubyneat/default_neat.rb +10 -0
- data/lib/rubyneat/dsl.rb +130 -0
- data/lib/rubyneat/evaluator.rb +51 -0
- data/lib/rubyneat/evolver.rb +315 -0
- data/lib/rubyneat/expressor.rb +110 -0
- data/lib/rubyneat/graph.rb +95 -0
- data/lib/rubyneat/neuron.rb +152 -0
- data/lib/rubyneat/population.rb +227 -0
- data/lib/rubyneat/rubyneat.rb +429 -0
- data/lib/rubyneat.rb +8 -0
- data/neater/invpend_neat.rb +150 -0
- data/neater/rnlib/inverted_pendulum.rb +380 -0
- data/neater/rnlib/xor.rb +10 -0
- data/neater/sigdebug_neat.rb +136 -0
- data/neater/xor_neat.rb +137 -0
- data/neater/xoranalog_neat.rb +138 -0
- data/neater/xorsin_neat.rb +143 -0
- data/projectFilesBackup/.idea/rubyneat.iml +180 -0
- data/public/.directory +4 -0
- data/public/background.png +0 -0
- data/public/background.xcf +0 -0
- data/public/cart.png +0 -0
- data/public/cart.xcf +0 -0
- data/public/metalpoles_molton_ball_l.jpg +0 -0
- data/public/old_background.png +0 -0
- data/public/pointer.png +0 -0
- data/public/pointer.xcf +0 -0
- data/public/pole.kra +0 -0
- data/public/pole.png +0 -0
- data/public/pole.xcf +0 -0
- data/public/wheel-of-year-stone-DD-131-WOYS.jpg +0 -0
- data/public/wheel.png +0 -0
- data/public/wheel.xcf +0 -0
- data/public/wood-planks-texture.jpg +0 -0
- data/rdoc/ControllerPoint.html +116 -0
- data/rdoc/CuteA.html +177 -0
- data/rdoc/CuteB.html +178 -0
- data/rdoc/DSLSetup.html +177 -0
- data/rdoc/GameTestWindow.html +242 -0
- data/rdoc/GameWindow.html +292 -0
- data/rdoc/Gemfile.html +215 -0
- data/rdoc/Gemfile_lock.html +327 -0
- data/rdoc/GraphTest.html +210 -0
- data/rdoc/Guardfile.html +198 -0
- data/rdoc/InvertedPendulum/Cart.html +668 -0
- data/rdoc/InvertedPendulum/DSL.html +259 -0
- data/rdoc/InvertedPendulum/InvPendWindow.html +402 -0
- data/rdoc/InvertedPendulum.html +198 -0
- data/rdoc/Logger.html +98 -0
- data/rdoc/NEAT/BasicNeuronTypes/BiasNeuron.html +265 -0
- data/rdoc/NEAT/BasicNeuronTypes/CosineNeuron.html +162 -0
- data/rdoc/NEAT/BasicNeuronTypes/InputNeuron.html +206 -0
- data/rdoc/NEAT/BasicNeuronTypes/SigmoidNeuron.html +162 -0
- data/rdoc/NEAT/BasicNeuronTypes/SineNeuron.html +162 -0
- data/rdoc/NEAT/BasicNeuronTypes/TanhNeuron.html +161 -0
- data/rdoc/NEAT/BasicNeuronTypes.html +107 -0
- data/rdoc/NEAT/Controller/NeatSettings.html +880 -0
- data/rdoc/NEAT/Controller.html +729 -0
- data/rdoc/NEAT/Critter/Genotype/Gene.html +457 -0
- data/rdoc/NEAT/Critter/Genotype.html +735 -0
- data/rdoc/NEAT/Critter/Phenotype.html +330 -0
- data/rdoc/NEAT/Critter.html +489 -0
- data/rdoc/NEAT/DSL.html +729 -0
- data/rdoc/NEAT/Evaluator.html +256 -0
- data/rdoc/NEAT/Evolver/CritterOp.html +349 -0
- data/rdoc/NEAT/Evolver.html +891 -0
- data/rdoc/NEAT/Expressor.html +402 -0
- data/rdoc/NEAT/Graph/DependencyResolver.html +291 -0
- data/rdoc/NEAT/Graph/GraphException.html +105 -0
- data/rdoc/NEAT/Graph.html +263 -0
- data/rdoc/NEAT/NeatException.html +105 -0
- data/rdoc/NEAT/NeatOb.html +325 -0
- data/rdoc/NEAT/Neuron.html +481 -0
- data/rdoc/NEAT/Operator.html +109 -0
- data/rdoc/NEAT/Population.html +935 -0
- data/rdoc/NEAT/Trait.html +117 -0
- data/rdoc/NEAT.html +422 -0
- data/rdoc/Object.html +384 -0
- data/rdoc/Phi.html +98 -0
- data/rdoc/Player.html +383 -0
- data/rdoc/Rakefile.html +254 -0
- data/rdoc/RubyNEAT/Application.html +105 -0
- data/rdoc/RubyNEAT.html +98 -0
- data/rdoc/SDL/Event2.html +98 -0
- data/rdoc/SDL.html +98 -0
- data/rdoc/Vector.html +195 -0
- data/rdoc/created.rid +125 -0
- data/rdoc/doc/ControllerPoint_html.html +299 -0
- data/rdoc/doc/CuteA_html.html +438 -0
- data/rdoc/doc/CuteB_html.html +436 -0
- data/rdoc/doc/DSL_html.html +992 -0
- data/rdoc/doc/NEAT/BasicNeuronTypes/BiasNeuron_html.html +617 -0
- data/rdoc/doc/NEAT/BasicNeuronTypes/CosineNeuron_html.html +413 -0
- data/rdoc/doc/NEAT/BasicNeuronTypes/InputNeuron_html.html +498 -0
- data/rdoc/doc/NEAT/BasicNeuronTypes/SigmoidNeuron_html.html +413 -0
- data/rdoc/doc/NEAT/BasicNeuronTypes/SineNeuron_html.html +413 -0
- data/rdoc/doc/NEAT/BasicNeuronTypes/TanhNeuron_html.html +412 -0
- data/rdoc/doc/NEAT/BasicNeuronTypes_html.html +310 -0
- data/rdoc/doc/NEAT/Controller/NeatSettings_html.html +3324 -0
- data/rdoc/doc/NEAT/Controller_html.html +2212 -0
- data/rdoc/doc/NEAT/Critter/Genotype/Gene_html.html +997 -0
- data/rdoc/doc/NEAT/Critter/Genotype_html.html +1556 -0
- data/rdoc/doc/NEAT/Critter/Phenotype_html.html +687 -0
- data/rdoc/doc/NEAT/Critter_html.html +1037 -0
- data/rdoc/doc/NEAT/DSL_html.html +1349 -0
- data/rdoc/doc/NEAT/Evaluator_html.html +556 -0
- data/rdoc/doc/NEAT/Evolver/CritterOp_html.html +690 -0
- data/rdoc/doc/NEAT/Evolver_html.html +677 -0
- data/rdoc/doc/NEAT/Expressor_html.html +468 -0
- data/rdoc/doc/NEAT/Graph/DependencyResolver_html.html +598 -0
- data/rdoc/doc/NEAT/Graph/GraphException_html.html +299 -0
- data/rdoc/doc/NEAT/Graph_html.html +527 -0
- data/rdoc/doc/NEAT/NeatException_html.html +299 -0
- data/rdoc/doc/NEAT/NeatOb_html.html +671 -0
- data/rdoc/doc/NEAT/Neuron_html.html +1095 -0
- data/rdoc/doc/NEAT/Operator_html.html +337 -0
- data/rdoc/doc/NEAT/Population_html.html +1795 -0
- data/rdoc/doc/NEAT/Trait_html.html +344 -0
- data/rdoc/doc/NEAT_html.html +736 -0
- data/rdoc/doc/_index_html.html +559 -0
- data/rdoc/doc/class_list_html.html +369 -0
- data/rdoc/doc/css/common_css.html +188 -0
- data/rdoc/doc/css/full_list_css.html +243 -0
- data/rdoc/doc/css/style_css.html +530 -0
- data/rdoc/doc/file_list_html.html +240 -0
- data/rdoc/doc/frames_html.html +217 -0
- data/rdoc/doc/index_html.html +559 -0
- data/rdoc/doc/js/app_js.html +423 -0
- data/rdoc/doc/js/full_list_js.html +372 -0
- data/rdoc/doc/js/jquery_js.html +1536 -0
- data/rdoc/doc/method_list_html.html +1375 -0
- data/rdoc/doc/top-level-namespace_html.html +317 -0
- data/rdoc/fonts/Lato-Light.ttf +0 -0
- data/rdoc/fonts/Lato-LightItalic.ttf +0 -0
- data/rdoc/fonts/Lato-Regular.ttf +0 -0
- data/rdoc/fonts/Lato-RegularItalic.ttf +0 -0
- data/rdoc/fonts/SourceCodePro-Bold.ttf +0 -0
- data/rdoc/fonts/SourceCodePro-Regular.ttf +0 -0
- data/rdoc/fonts.css +167 -0
- data/rdoc/images/add.png +0 -0
- data/rdoc/images/arrow_up.png +0 -0
- data/rdoc/images/brick.png +0 -0
- data/rdoc/images/brick_link.png +0 -0
- data/rdoc/images/bug.png +0 -0
- data/rdoc/images/bullet_black.png +0 -0
- data/rdoc/images/bullet_toggle_minus.png +0 -0
- data/rdoc/images/bullet_toggle_plus.png +0 -0
- data/rdoc/images/date.png +0 -0
- data/rdoc/images/delete.png +0 -0
- data/rdoc/images/find.png +0 -0
- data/rdoc/images/loadingAnimation.gif +0 -0
- data/rdoc/images/macFFBgHack.png +0 -0
- data/rdoc/images/package.png +0 -0
- data/rdoc/images/page_green.png +0 -0
- data/rdoc/images/page_white_text.png +0 -0
- data/rdoc/images/page_white_width.png +0 -0
- data/rdoc/images/plugin.png +0 -0
- data/rdoc/images/ruby.png +0 -0
- data/rdoc/images/tag_blue.png +0 -0
- data/rdoc/images/tag_green.png +0 -0
- data/rdoc/images/transparent.png +0 -0
- data/rdoc/images/wrench.png +0 -0
- data/rdoc/images/wrench_orange.png +0 -0
- data/rdoc/images/zoom.png +0 -0
- data/rdoc/index.html +282 -0
- data/rdoc/js/darkfish.js +140 -0
- data/rdoc/js/jquery.js +18 -0
- data/rdoc/js/navigation.js +142 -0
- data/rdoc/js/search.js +109 -0
- data/rdoc/js/search_index.js +1 -0
- data/rdoc/js/searcher.js +228 -0
- data/rdoc/rdoc.css +580 -0
- data/rdoc/rubyneat_gemspec.html +387 -0
- data/rdoc/table_of_contents.html +2502 -0
- data/rdoc/xordebug_log.html +170598 -0
- data/rdoc/xorsin_log.html +22569 -0
- data/rubyneat.gemspec +347 -0
- data/rubyneat.gemspec.orig +375 -0
- data/spec/lib/rubyneat/rubyneat_spec.rb +132 -0
- metadata +555 -0
@@ -0,0 +1,227 @@
|
|
1
|
+
require 'rubyneat/rubyneat'
|
2
|
+
|
3
|
+
module NEAT
|
4
|
+
#= Population of NEAT Critters.
|
5
|
+
# The Population
|
6
|
+
# In ourselves we have the pool of neurons the critters all use.
|
7
|
+
# the pool of neurons are indirects, of course, as during phenotype
|
8
|
+
# expression, all the phenotypes shall be created individually.
|
9
|
+
#
|
10
|
+
class Population < NeatOb
|
11
|
+
# Ordered list or hash of input neuron classes
|
12
|
+
# (all critters generated here shall have this)
|
13
|
+
attr_accessor :input_neurons
|
14
|
+
|
15
|
+
# List of possible neuron classes for hidden neurons.
|
16
|
+
attr_accessor :hidden_neurons
|
17
|
+
|
18
|
+
# Ordered list or hash of output neuron classes
|
19
|
+
# (all critters generated here shall have this)
|
20
|
+
attr_accessor :output_neurons
|
21
|
+
|
22
|
+
attr_accessor :traits
|
23
|
+
|
24
|
+
# list of critter in this population
|
25
|
+
attr_accessor :critters
|
26
|
+
|
27
|
+
# Overall population fitness and novelty
|
28
|
+
attr_reader :fitness, :novelty
|
29
|
+
|
30
|
+
# Hash list of species lists
|
31
|
+
attr_reader :species
|
32
|
+
|
33
|
+
# in a deep dive, exclude the following from replication.
|
34
|
+
exclude :input_neurons, :output_neurons
|
35
|
+
|
36
|
+
# Create initial (ramdom) population of critters
|
37
|
+
def initialize(c, &block)
|
38
|
+
super
|
39
|
+
@input_neurons = c.neural_inputs.clone
|
40
|
+
@output_neurons = c.neural_outputs.clone
|
41
|
+
@hidden_neurons = unless c.neural_hidden.nil?
|
42
|
+
c.neural_hidden
|
43
|
+
else
|
44
|
+
c.neuron_catalog.keep_if {|n| not n.input?}
|
45
|
+
end
|
46
|
+
@critters = (0 ... c.parms.start_population_size || c.parms.population_size).map do
|
47
|
+
Critter.new(self)
|
48
|
+
end
|
49
|
+
block.(self) unless block.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
# Make sure all critters are reset and prepared for
|
53
|
+
# recurrent network evaluation.
|
54
|
+
def initialize_for_recurrence!
|
55
|
+
@critters.each {|crit| crit.initialize_neurons!}
|
56
|
+
end
|
57
|
+
|
58
|
+
# Mutate the genes and neurons.
|
59
|
+
def mutate!
|
60
|
+
@controller.evolver.mutate! self
|
61
|
+
end
|
62
|
+
|
63
|
+
# Express the entire population.
|
64
|
+
def express!
|
65
|
+
@critters.each { |critter| critter.express! }
|
66
|
+
end
|
67
|
+
|
68
|
+
# Called for each sequence.
|
69
|
+
def evaluate!
|
70
|
+
@critters.each { |critter| critter.evaluate! }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Alalyze evaluation results.
|
74
|
+
def analyze!
|
75
|
+
@critters.each { |critter| @controller.evaluator.analyze_for_fitness! critter }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Call this after evaluation.
|
79
|
+
# Returns a newly-evolved population.
|
80
|
+
def evolve
|
81
|
+
@controller.evolver.evolve self
|
82
|
+
end
|
83
|
+
|
84
|
+
# Group critters into species
|
85
|
+
# Note that the @species objects
|
86
|
+
# have useful singleton methods:
|
87
|
+
#* @species.member? -- checks all of the lists for membership, not just the hash
|
88
|
+
#* @species[crit].fitness -- fitness of the entire species
|
89
|
+
def speciate!
|
90
|
+
# We blow away existing species and create our own member? function
|
91
|
+
@species = {} # lists keyed by representative critter
|
92
|
+
def @species.member?(crit)
|
93
|
+
super.member?(crit) or self.map{|k, li| li.member? crit}.reduce{|t1, t2| t1 or t2 }
|
94
|
+
end
|
95
|
+
|
96
|
+
def @species.evaluate!
|
97
|
+
self.each do |k, sp|
|
98
|
+
sp.fitness = sp.map{|crit| crit.fitness}.reduce{|a,b| a+b} / sp.size
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def @species.compactify!(parm)
|
103
|
+
mutt = self[:mutt] = self.map { |k, splist| [k, splist]}.reject {|k, splist|
|
104
|
+
splist.size >= parm.smallest_species
|
105
|
+
}.map { |k, splist|
|
106
|
+
self.delete k
|
107
|
+
splist
|
108
|
+
}.flatten
|
109
|
+
|
110
|
+
# FIXME this code is not dry!!!!
|
111
|
+
def mutt.fitness=(fit)
|
112
|
+
@fitness = fit
|
113
|
+
end
|
114
|
+
|
115
|
+
def mutt.fitness
|
116
|
+
@fitness
|
117
|
+
end
|
118
|
+
|
119
|
+
self.delete :mutt if self[:mutt].empty?
|
120
|
+
end
|
121
|
+
|
122
|
+
# Some convience parms
|
123
|
+
parm = @controller.parms
|
124
|
+
|
125
|
+
# And so now we iterate...
|
126
|
+
@critters.each do |crit|
|
127
|
+
wearein = false
|
128
|
+
@species.each do |ck, list|
|
129
|
+
delta = crit.compare(ck)
|
130
|
+
#log.debug { "delta for #{crit} and #{ck} is #{delta}" }
|
131
|
+
if delta < parm.compatibility_threshold
|
132
|
+
list << crit
|
133
|
+
wearein = true
|
134
|
+
break
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# New species?
|
139
|
+
unless wearein
|
140
|
+
@species[crit] = species = [crit]
|
141
|
+
def species.fitness=(fit)
|
142
|
+
@fitness = fit
|
143
|
+
end
|
144
|
+
def species.fitness
|
145
|
+
@fitness
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Compactify the species if less than smallest_species
|
151
|
+
@species.compactify! parm
|
152
|
+
|
153
|
+
# And now we evaluate all species for fitness...
|
154
|
+
@species.evaluate!
|
155
|
+
|
156
|
+
# Dump for debugging reasons
|
157
|
+
@species.each do |k, sp|
|
158
|
+
log.debug ">> Species #{k} has #{sp.size} members with a #{sp.fitness} fitness"
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
#== Generate a report on the state of this population.
|
164
|
+
#
|
165
|
+
def report
|
166
|
+
{
|
167
|
+
fitness: report_fitness,
|
168
|
+
fitness_species: report_fitness_species,
|
169
|
+
best_critter: report_best_fit,
|
170
|
+
worst_critter: report_worst_fit,
|
171
|
+
}
|
172
|
+
end
|
173
|
+
|
174
|
+
# The "best critter" is the critter with the lowest (closet to zero)
|
175
|
+
# fitness rating.
|
176
|
+
def best_critter
|
177
|
+
unless @controller.compare_func.nil?
|
178
|
+
@critters.min {|a, b| @controller.compare_func.(a.fitness, b.fitness) }
|
179
|
+
else
|
180
|
+
@critters.min {|a, b| a.fitness <=> b.fitness}
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# The "worst critter" is the critter with the highest (away from zero)
|
185
|
+
# fitness rating.
|
186
|
+
def worst_critter
|
187
|
+
unless @controller.compare_func.nil?
|
188
|
+
@critters.max {|a, b| @controller.compare_func.(a.fitness, b.fitness) }
|
189
|
+
else
|
190
|
+
@critters.max {|a, b| a.fitness <=> b.fitness}
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def dump_s
|
195
|
+
to_s + "\npopulation:\n" + @critters.map{|crit| crit.dump_s }.join("\n")
|
196
|
+
end
|
197
|
+
|
198
|
+
protected
|
199
|
+
# report on many fitness metrics
|
200
|
+
def report_fitness
|
201
|
+
{
|
202
|
+
overall: @critters.map{|critter| critter.fitness}.reduce{|m, f| m + f} / @critters.size,
|
203
|
+
best: best_critter.fitness,
|
204
|
+
worst: worst_critter.fitness,
|
205
|
+
}
|
206
|
+
end
|
207
|
+
|
208
|
+
# report on the best and worst species
|
209
|
+
def report_fitness_species
|
210
|
+
{
|
211
|
+
best: nil,
|
212
|
+
worst: nil,
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
# Find the best fit critter
|
217
|
+
def report_best_fit
|
218
|
+
best_critter.phenotype.code
|
219
|
+
end
|
220
|
+
|
221
|
+
# Find the worst fit critter
|
222
|
+
def report_worst_fit
|
223
|
+
worst_critter.phenotype.code
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,429 @@
|
|
1
|
+
require 'distribution'
|
2
|
+
require 'yaml'
|
3
|
+
require 'logger'
|
4
|
+
require 'awesome_print'
|
5
|
+
require 'deep_dive'
|
6
|
+
|
7
|
+
=begin rdoc
|
8
|
+
= RubyNEAT -- a Ruby Implementation of the NeuroEvolution by Augmented Topologies.
|
9
|
+
|
10
|
+
The RubyNEAT system incorporates the basis of the NEAT alorithm. Flexibility
|
11
|
+
is the key here, allowing RubyNEAT to be leverage in a varitety of applications.
|
12
|
+
|
13
|
+
=== Requirements
|
14
|
+
We make no effort to support Ruby versions less than 1.9.2. I know this will
|
15
|
+
be a problem to some, but you are strongly urgerd to upgrade.
|
16
|
+
|
17
|
+
=end
|
18
|
+
|
19
|
+
$log = Logger.new(STDOUT)
|
20
|
+
$log.level = Logger::INFO
|
21
|
+
AwesomePrint.defaults = { plain: true }
|
22
|
+
|
23
|
+
=begin rdoc
|
24
|
+
= NEAT -- Module for RubyNEAT.
|
25
|
+
|
26
|
+
== Synopsis
|
27
|
+
We have a Population of Critters, and each Critter
|
28
|
+
represents a network of Neurons and a connection list specifying
|
29
|
+
how those Neurons are connected.
|
30
|
+
|
31
|
+
Each Neuron has an inplicit genotype and phenotype component. Neurons,
|
32
|
+
from the Ruby persoective, contain their own code to produce their own
|
33
|
+
phenotypes.
|
34
|
+
|
35
|
+
There are input Neurons and output Neurons. The input Neurons are special, as
|
36
|
+
they do not contain any input from other nodes, but serve as interfaces
|
37
|
+
from the "real world". Thier range of inputs are open, and it shall be up to
|
38
|
+
the input Neuron's phenotype generators to condition those inputs, if need be,
|
39
|
+
to someething more suiable for the neural network.
|
40
|
+
|
41
|
+
== Issues
|
42
|
+
=== Multicore / Cloud Computing
|
43
|
+
Some thought needs to be given to how to make this anenable to multiple
|
44
|
+
processes so that we can leverage the power of multicore systems as well
|
45
|
+
as multiple computers in the Cloud, etc.
|
46
|
+
|
47
|
+
Our initial inclination is to put all of that functionality in the Conroller.
|
48
|
+
=end
|
49
|
+
module NEAT
|
50
|
+
@rng_count = 0
|
51
|
+
@rng_names = %w{aaa bee cex dee flo kis lee mor cie lou gir sex quo sam lac hin pee
|
52
|
+
cur set sew flat nac zac pae por lie lox pox nez fez wib poo sho
|
53
|
+
nuz tux que bsh shi her him can muk fuk kit kat uno dos ant mic
|
54
|
+
aa be nz oo py tt my of ze mu pi zz qu fl tr as sd fg gh hj bc
|
55
|
+
lion tame monk busy honk tape slap zonk funk tear flip shop soap
|
56
|
+
quay mony stir moot shoo slim fate trat beep kook love hate
|
57
|
+
mire hair lips funk open shut case lace joop lute doze fuzz
|
58
|
+
mean nice soil vote kick apes snak huge sine pine gray nook fool
|
59
|
+
woot hail smel tell jell suut gage phat pinoy spain rey bloke zit}
|
60
|
+
def self.random_name_generator
|
61
|
+
(1..3).map {
|
62
|
+
@rng_names[rand @rng_names.size]
|
63
|
+
}.push(@rng_count += 1).join('_').to_sym
|
64
|
+
end
|
65
|
+
|
66
|
+
# Name of the stimulus method in NEAT::Critter::Phenotype to use
|
67
|
+
# for the singleton method expression of the critter.
|
68
|
+
STIMULUS = :stimulate
|
69
|
+
|
70
|
+
# Mixin for new innovation numbers.
|
71
|
+
def self.new_innovation; @controller.new_innovation; end
|
72
|
+
|
73
|
+
# Mixin for the gaussian object.
|
74
|
+
def self.gaussian ; @controller.gaussian; end
|
75
|
+
|
76
|
+
# PrettyPrint to log.debug
|
77
|
+
def self.dpp ob
|
78
|
+
#$log.ap ob
|
79
|
+
end
|
80
|
+
|
81
|
+
# Basis of all NEAT objects
|
82
|
+
class NeatOb
|
83
|
+
include DeepDive
|
84
|
+
exclude :controller, :name
|
85
|
+
|
86
|
+
# Designation of this particular object instance
|
87
|
+
attr_reader :name
|
88
|
+
|
89
|
+
# Who's your daddy?
|
90
|
+
attr_reader :controller
|
91
|
+
|
92
|
+
def log ; $log ; end
|
93
|
+
def self.log ; $log; end
|
94
|
+
|
95
|
+
# Initializer for all NEAT objects. Requires that
|
96
|
+
# the controller object is specified for all classes
|
97
|
+
# with the exception of the Controller itself or the
|
98
|
+
# Controller's NeatSettings.
|
99
|
+
def initialize(controller = nil, name = nil)
|
100
|
+
@name = unless name.nil?
|
101
|
+
name.to_sym
|
102
|
+
else
|
103
|
+
NEAT::random_name_generator
|
104
|
+
end
|
105
|
+
unless controller.nil?
|
106
|
+
@controller = controller
|
107
|
+
else
|
108
|
+
raise NeatException.new "Controller Needed!" unless self.is_a?(Controller) or self.is_a?(Controller::NeatSettings)
|
109
|
+
@controller = self unless self.is_a? Controller::NeatSettings
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_s
|
114
|
+
"%s<%s>" % [self.class, self.name]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class NeatException < Exception
|
119
|
+
end
|
120
|
+
|
121
|
+
#= Base class of operators in RubyNEAT,
|
122
|
+
# Such as Evolver, etc.
|
123
|
+
class Operator < NeatOb
|
124
|
+
end
|
125
|
+
|
126
|
+
#= Traits
|
127
|
+
# A Trait is a group of parameters that can be expressed
|
128
|
+
# as a group more than one time. Traits save a genetic
|
129
|
+
# algorithm from having to search vast parameter landscapes
|
130
|
+
# on every node. Instead, each node can simply point to a trait
|
131
|
+
# and those traits can evolve on their own. (Taken from the C version of NEAT)
|
132
|
+
#
|
133
|
+
# Since we wish to allow for different classes of Neurons, this trait idea is
|
134
|
+
# super, since all we need to do is have a different trait species for the
|
135
|
+
# different node types.
|
136
|
+
class Trait < NeatOb
|
137
|
+
end
|
138
|
+
|
139
|
+
require 'rubyneat/critter'
|
140
|
+
require 'rubyneat/neuron'
|
141
|
+
require 'rubyneat/population'
|
142
|
+
require 'rubyneat/evolver'
|
143
|
+
require 'rubyneat/expressor'
|
144
|
+
require 'rubyneat/evaluator'
|
145
|
+
|
146
|
+
#= Controller for all operations of RubyNEAT
|
147
|
+
# This object contains all the specifications and details for
|
148
|
+
# evolving and evaluation of the RubyNEAT system. It is
|
149
|
+
# a type of "World", if you will, for the entire enterprise.
|
150
|
+
#
|
151
|
+
# Your application shall only have one Controller.
|
152
|
+
class Controller < NeatOb
|
153
|
+
# global innovation number
|
154
|
+
attr_reader :glob_innov_num
|
155
|
+
|
156
|
+
# current sequence number being evaluated
|
157
|
+
attr_reader :seq_num
|
158
|
+
|
159
|
+
# Current generation count
|
160
|
+
attr_reader :generation_num
|
161
|
+
|
162
|
+
# catalog of neurons classes to use { weight => nclass, ... }
|
163
|
+
attr_accessor :neuron_catalog
|
164
|
+
|
165
|
+
# Class map of named input and output neurons (each critter will have
|
166
|
+
# instantiations of these) name: InputNeuralClass (usually InputNeuron)
|
167
|
+
attr_accessor :neural_inputs, :neural_outputs, :neural_hidden
|
168
|
+
|
169
|
+
# Parameters for evolution (NeatParameters)
|
170
|
+
attr_accessor :parms
|
171
|
+
|
172
|
+
# population object and class specification
|
173
|
+
attr_reader :population, :population_history, :population_class
|
174
|
+
|
175
|
+
attr_accessor :expressor, :expressor_class
|
176
|
+
attr_accessor :evaluator, :evaluator_class
|
177
|
+
attr_accessor :evolver, :evolver_class
|
178
|
+
|
179
|
+
# Global verbosity level:
|
180
|
+
## 1 - normal (the default)
|
181
|
+
## 2 - really verbose
|
182
|
+
## 3 - maximally verbose
|
183
|
+
# Use in conjunction with log.debug
|
184
|
+
attr_accessor :verbosity
|
185
|
+
|
186
|
+
# Query function that Critters shall call.
|
187
|
+
attr_accessor :query_func
|
188
|
+
|
189
|
+
# Fitness function that Critters shall be rated on.
|
190
|
+
attr_accessor :fitness_func
|
191
|
+
|
192
|
+
# Recurrence function that Critters will yield to.
|
193
|
+
attr_accessor :recurrence_func
|
194
|
+
|
195
|
+
# Compare function for fitness
|
196
|
+
# Cost function for integrating in the cost to the fitness scalar.
|
197
|
+
attr_accessor :compare_func, :cost_func, :stop_on_fit_func
|
198
|
+
|
199
|
+
# End run function to call at the end of each generational run
|
200
|
+
# Also report_hook to dump reports for the user, etc.
|
201
|
+
attr_accessor :end_run_func, :report_hook
|
202
|
+
|
203
|
+
# Logger object for all of RubyNEAT
|
204
|
+
attr_reader :log
|
205
|
+
|
206
|
+
# Various parameters affecting evolution.
|
207
|
+
# Based somewhat on the C version of NEAT.
|
208
|
+
# TODO not all of these parameters are implemented yet!!!
|
209
|
+
class NeatSettings < NeatOb
|
210
|
+
## RubyNEAT specific
|
211
|
+
|
212
|
+
# Set to true to returned named parameters as hashes to the fitness function
|
213
|
+
# (the default is to do ordered arrays)
|
214
|
+
attr_accessor :hash_on_fitness
|
215
|
+
|
216
|
+
## based on the C version of NEAT
|
217
|
+
attr_accessor :age_significance
|
218
|
+
attr_accessor :babies_stolen
|
219
|
+
|
220
|
+
# Species compatability threshold
|
221
|
+
attr_accessor :compatibility_threshold
|
222
|
+
|
223
|
+
# Speciation coffficient
|
224
|
+
attr_accessor :disjoint_coefficient, :excess_coefficient, :weight_coefficient
|
225
|
+
|
226
|
+
# Max target number of species (will result in the compatability_coeifficient
|
227
|
+
# being adjusted automatically
|
228
|
+
attr_accessor :max_species
|
229
|
+
|
230
|
+
# Species Peality age for not making progress
|
231
|
+
attr_accessor :dropoff_age
|
232
|
+
|
233
|
+
# Species smallest population allowed (coalse smaller species into one)
|
234
|
+
attr_accessor :smallest_species
|
235
|
+
|
236
|
+
# Ratio of mating are actually interspecies
|
237
|
+
attr_accessor :interspecies_mate_rate
|
238
|
+
|
239
|
+
attr_accessor :linktrait_mutation_sig
|
240
|
+
attr_accessor :mate_multipoint_avg_prob
|
241
|
+
attr_accessor :mate_multipoint_prob
|
242
|
+
attr_accessor :mate_only_prob
|
243
|
+
attr_accessor :mate_singlepoint_prob
|
244
|
+
|
245
|
+
# Maximum number of generations to run, if given.
|
246
|
+
attr_accessor :max_generations
|
247
|
+
|
248
|
+
# Maximun number of populations to maintain in the history buffer.
|
249
|
+
attr_accessor :max_population_history
|
250
|
+
|
251
|
+
attr_accessor :mutate_add_gene_prob
|
252
|
+
attr_accessor :mutate_add_neuron_prob
|
253
|
+
|
254
|
+
attr_accessor :mutate_gene_disable_prob
|
255
|
+
attr_accessor :mutate_gene_reenable_prob
|
256
|
+
|
257
|
+
attr_accessor :mutate_gene_trait_prob
|
258
|
+
|
259
|
+
# For gene weights perturbations and changes (complete overwrites)
|
260
|
+
attr_accessor :mutate_perturb_gene_weights_prob,
|
261
|
+
:mutate_perturb_gene_weights_sd,
|
262
|
+
:mutate_change_gene_weights_prob,
|
263
|
+
:mutate_change_gene_weights_sd
|
264
|
+
|
265
|
+
attr_accessor :mutate_neuron_trait_prob
|
266
|
+
attr_accessor :mutate_only_prob
|
267
|
+
attr_accessor :mutate_random_trait_prob
|
268
|
+
attr_accessor :mutate_toggle_enable_prob
|
269
|
+
attr_accessor :mutdiff_coefficient
|
270
|
+
attr_accessor :newlink_tries
|
271
|
+
attr_accessor :neuron_trait_mut_sig
|
272
|
+
|
273
|
+
# fitness costs, if given, use in the computation of fitness
|
274
|
+
# AFTER the overall fitness for the applied stimuli have been
|
275
|
+
# caclulated.
|
276
|
+
attr_accessor :fitness_cost_per_neuron
|
277
|
+
attr_accessor :fitness_cost_per_gene
|
278
|
+
|
279
|
+
# If set, will start off at the specified size and
|
280
|
+
# grow to the bigger population size
|
281
|
+
attr_accessor :start_population_size, :population_size
|
282
|
+
|
283
|
+
attr_accessor :start_sequence_at, :end_sequence_at
|
284
|
+
|
285
|
+
attr_accessor :print_every
|
286
|
+
attr_accessor :recur_only_prob
|
287
|
+
attr_accessor :recur_prob
|
288
|
+
|
289
|
+
# factor (0 to 1) of the top percentage of the species that's
|
290
|
+
# allowed to mate.
|
291
|
+
attr_accessor :survival_threshold
|
292
|
+
attr_accessor :survival_mininum_per_species
|
293
|
+
|
294
|
+
attr_accessor :trait_mutation_power
|
295
|
+
attr_accessor :trait_param_mut_prob
|
296
|
+
attr_accessor :weigh_mut_power
|
297
|
+
|
298
|
+
# Enable FS-NEAT
|
299
|
+
attr_accessor :feature_selection_switch
|
300
|
+
|
301
|
+
# Enable HyperNEAT. This will result in the critters
|
302
|
+
# being interpreted as CPPNs for substrate weights. Additional
|
303
|
+
# setup will be necessary.
|
304
|
+
attr_accessor :hyper_switch
|
305
|
+
|
306
|
+
# Enable Evolved Substrate HyperNEAT. Meaningless unless
|
307
|
+
# hyper_switch is also enabled.
|
308
|
+
attr_accessor :evolved_substrate_switch
|
309
|
+
|
310
|
+
# Enable RT-NEAT, for gradual evolution suitable for
|
311
|
+
# games and other human-interactive systems.
|
312
|
+
attr_accessor :real_time_switch
|
313
|
+
|
314
|
+
# If true, allow for recurrent networks.
|
315
|
+
attr_accessor :recurrency_switch
|
316
|
+
|
317
|
+
# Set up defaults for mandatory entries.
|
318
|
+
def initialize
|
319
|
+
super
|
320
|
+
@start_sequence_at = 0
|
321
|
+
@end_sequence_at = 100
|
322
|
+
@max_generations = 1000
|
323
|
+
|
324
|
+
# Default operators
|
325
|
+
@evaluator = Evaluator.new self
|
326
|
+
@expressor = Expressor.new self
|
327
|
+
@evolver = Evolver.new self
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
#- neural_inputs -- array of input classes
|
332
|
+
#- neural_outputs -- array of output classes
|
333
|
+
#- parameters -- NeatParameters object, or a path to a YAML file to create this.
|
334
|
+
def initialize(neural_inputs: nil,
|
335
|
+
neural_outputs: nil,
|
336
|
+
neural_hidden: nil,
|
337
|
+
parameters: NeatSettings.new,
|
338
|
+
&block)
|
339
|
+
super(self)
|
340
|
+
@verbosity = 1
|
341
|
+
@glob_innov_num = 0
|
342
|
+
@gaussian = Distribution::Normal.rng
|
343
|
+
@population_history = []
|
344
|
+
@evolver = Evolver.new self
|
345
|
+
@expressor = Expressor.new self
|
346
|
+
|
347
|
+
@neuron_catalog = Neuron::neuron_types.clone
|
348
|
+
@neural_inputs = neural_inputs
|
349
|
+
@neural_outputs = neural_outputs
|
350
|
+
@neural_hidden = neural_hidden
|
351
|
+
|
352
|
+
# Default classes for population and operators, etc.
|
353
|
+
@population_class = NEAT::Population
|
354
|
+
@evaluator_class = NEAT::Evaluator
|
355
|
+
@expressor_class = NEAT::Expressor
|
356
|
+
@evolver_class = NEAT::Evolver
|
357
|
+
|
358
|
+
# Handle the parameters parameter. :-)
|
359
|
+
@parms = unless parameters.kind_of? String
|
360
|
+
parameters
|
361
|
+
else # load it from a file
|
362
|
+
open(parameters, 'r') { |fd| YAML::load fd.read }
|
363
|
+
end
|
364
|
+
block.(self) unless block.nil?
|
365
|
+
end
|
366
|
+
|
367
|
+
def new_innovation ; @glob_innov_num += 1; end
|
368
|
+
def gaussian ; @gaussian.() ; end
|
369
|
+
|
370
|
+
# Run this evolution.
|
371
|
+
def run
|
372
|
+
pre_run_initialize
|
373
|
+
(1..@parms.max_generations).each do |gen_number|
|
374
|
+
@generation_num = gen_number
|
375
|
+
@population_history << unless @population.nil?
|
376
|
+
@population
|
377
|
+
else
|
378
|
+
@population = @population_class.new(self)
|
379
|
+
end
|
380
|
+
@population_history.shift unless @population_history.size <= @parms.max_population_history
|
381
|
+
@population.mutate!
|
382
|
+
@population.express!
|
383
|
+
|
384
|
+
## Evaluate population
|
385
|
+
@evaluator.ready_for_evaluation @population
|
386
|
+
(@parms.start_sequence_at .. @parms.end_sequence_at).each do |snum|
|
387
|
+
@seq_num = snum
|
388
|
+
@population.evaluate!
|
389
|
+
end
|
390
|
+
|
391
|
+
@population.analyze!
|
392
|
+
@population.speciate!
|
393
|
+
|
394
|
+
$log.debug @population.dump_s unless @verbosity < 3
|
395
|
+
|
396
|
+
new_pop = @population.evolve
|
397
|
+
|
398
|
+
## Report hook for evaluation
|
399
|
+
@report_hook.(@population.report) unless @report_hook.nil?
|
400
|
+
|
401
|
+
## Exit if fitness criteria is reached
|
402
|
+
#FIXME handle this exit condition better!!!!!
|
403
|
+
exit if @stop_on_fit_func.(@population.report[:fitness], self) unless @stop_on_fit_func.nil?
|
404
|
+
|
405
|
+
## Evolve population
|
406
|
+
@population = new_pop
|
407
|
+
|
408
|
+
## Finish up this run
|
409
|
+
@end_run_func.(self) unless @end_run_func.nil?
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
private
|
414
|
+
# We must set up the objects we need prior to the run, if not set.
|
415
|
+
def pre_run_initialize
|
416
|
+
@evaluator = @evaluator_class.new(self) if @evaluator.nil?
|
417
|
+
@evolver = @evolver_class.new(self) if @evolver.nil?
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
@controller = Controller.new
|
422
|
+
def self.controller ; @controller ; end
|
423
|
+
def self.controller=(controller) ; @controller = controller ; end
|
424
|
+
def self.create_controller(*parms); @controller = Controller.new(*parms); end
|
425
|
+
end
|
426
|
+
|
427
|
+
# We put all the internal requires at the end to avoid conflicts.
|
428
|
+
require 'rubyneat/neuron'
|
429
|
+
require 'rubyneat/population'
|