rubyneat 0.3.5.alpha.2
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 +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'
|