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,374 @@
|
|
|
1
|
+
require 'rubyneat'
|
|
2
|
+
|
|
3
|
+
=begin rdoc
|
|
4
|
+
= Critter
|
|
5
|
+
=end
|
|
6
|
+
module NEAT
|
|
7
|
+
|
|
8
|
+
#= Critters for NEAT
|
|
9
|
+
# The Critter class comprises a Genotype and a Phenotype.
|
|
10
|
+
# The Genotype comprises Genes and Neurons.
|
|
11
|
+
class Critter < NeatOb
|
|
12
|
+
attr_reader :population
|
|
13
|
+
attr_accessor :genotype, :phenotype
|
|
14
|
+
|
|
15
|
+
# Ratings assigned by Evaluator
|
|
16
|
+
attr_accessor :fitness, :novelty
|
|
17
|
+
|
|
18
|
+
# Critter construction. We construct the genotype.
|
|
19
|
+
# The phenotype will be constructed by the Expressor operator.
|
|
20
|
+
def initialize(pop, mating = false, &block)
|
|
21
|
+
super pop.controller
|
|
22
|
+
@population = pop
|
|
23
|
+
@genotype = Genotype.new(self, mating)
|
|
24
|
+
block.(self) unless block.nil?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Get the Critter ready for the Expressor to
|
|
28
|
+
# express the geneotype.
|
|
29
|
+
def ready_for_expression!
|
|
30
|
+
@genotype.wire!
|
|
31
|
+
@phenotype = NEAT::Critter::Phenotype[self]
|
|
32
|
+
@phenotype
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Exoress this critter using the Expressor plugin.
|
|
36
|
+
def express!
|
|
37
|
+
@controller.expressor.express! self
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# This initializes neurons in preparation for recurrence.
|
|
41
|
+
# Note that the Critter should already have expressed its
|
|
42
|
+
# genotype before this is called.
|
|
43
|
+
def initialize_neurons!
|
|
44
|
+
@phenotype.initialize_neurons
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# A single evaluation step. Evaluate and
|
|
48
|
+
# generate fitness, novelty, etc.
|
|
49
|
+
# Returns the result.
|
|
50
|
+
def evaluate!
|
|
51
|
+
@controller.evaluator.evaluate! self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#= Genotype part of the Critter
|
|
55
|
+
# List of connections, basically.
|
|
56
|
+
#
|
|
57
|
+
# Also, basic phentypic expression (which may be overriden by
|
|
58
|
+
# the expressor)
|
|
59
|
+
#
|
|
60
|
+
#= Notes
|
|
61
|
+
# Currently, all lists of neurons and genes are Hashes. The
|
|
62
|
+
# neurons are indexed by their own names, and the genes
|
|
63
|
+
# are indexed by their innovation numbers.
|
|
64
|
+
#
|
|
65
|
+
class Genotype < NeatOb
|
|
66
|
+
# Critter to which we belong
|
|
67
|
+
attr_accessor :critter
|
|
68
|
+
|
|
69
|
+
# Genes keyed by innovation numbers
|
|
70
|
+
attr_accessor :genes
|
|
71
|
+
|
|
72
|
+
# List of neurons hashed by name
|
|
73
|
+
attr_accessor :neurons
|
|
74
|
+
|
|
75
|
+
# Instantiations of neural inputs and outputs
|
|
76
|
+
attr_reader :neural_inputs, :neural_outputs
|
|
77
|
+
|
|
78
|
+
# This will be set to true if there are dangling neurons.
|
|
79
|
+
attr_accessor :dangling_neurons
|
|
80
|
+
alias :dangling_neurons? :dangling_neurons
|
|
81
|
+
|
|
82
|
+
# Map neurons to the genes that marks them as output
|
|
83
|
+
# { oneu_name => [ gene_1, gene_2,... gene_n], ...}
|
|
84
|
+
# Just take the in_neuron name and the weight to do
|
|
85
|
+
# the call to that neuron function with the appropriate weights
|
|
86
|
+
attr_reader :neural_gene_map
|
|
87
|
+
|
|
88
|
+
def initialize(critter, mating = false, &block)
|
|
89
|
+
super critter.controller
|
|
90
|
+
@critter = critter
|
|
91
|
+
|
|
92
|
+
# Initialize basic structures
|
|
93
|
+
@genes = nil
|
|
94
|
+
@neural_inputs = Hash[@critter.population.input_neurons.map { |sym, ineu|
|
|
95
|
+
[sym, ineu.new(@controller, sym)]
|
|
96
|
+
}]
|
|
97
|
+
|
|
98
|
+
@neural_outputs = Hash[@critter.population.output_neurons.map { |sym, ineu|
|
|
99
|
+
[sym, ineu.new(@controller, sym)]
|
|
100
|
+
}]
|
|
101
|
+
@neurons = @neural_inputs.clone # this must be a shallow clone!
|
|
102
|
+
@neurons.merge! @neural_outputs
|
|
103
|
+
|
|
104
|
+
@controller.evolver.gen_initial_genes!(self) unless mating
|
|
105
|
+
block.(self) unless block.nil?
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# We add genes given here to the genome.
|
|
109
|
+
# An array of genes is returned from the block
|
|
110
|
+
# and we simply add them in.
|
|
111
|
+
# @param [boolean] clean
|
|
112
|
+
# @param [Proc] block
|
|
113
|
+
def neucleate(clean: true, &block)
|
|
114
|
+
genes = Hash[block.(self).map { |g|
|
|
115
|
+
g.genotype = self
|
|
116
|
+
[g.innovation, g] }]
|
|
117
|
+
if clean
|
|
118
|
+
@genes = genes
|
|
119
|
+
else
|
|
120
|
+
@genes.merge! genes
|
|
121
|
+
end
|
|
122
|
+
nuke_redundancies!
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Remove any redundancies in the genome,
|
|
126
|
+
# any genes refering to the same two neurons.
|
|
127
|
+
# Simply choose one and delete the rest.
|
|
128
|
+
# TODO: implement nuke_redundancies!
|
|
129
|
+
def nuke_redundancies!
|
|
130
|
+
log.warn 'nuke_redundancies! NIY'
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Make the neurons forget their wiring.
|
|
134
|
+
def forget!
|
|
135
|
+
@neurons.each { |name, neu| neu.clear_graph }
|
|
136
|
+
@neural_gene_map = Hash.new {|h, k| h[k] = [] }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Wire up the neurons based on the genes.
|
|
140
|
+
def wire!
|
|
141
|
+
forget!
|
|
142
|
+
@genes.each do |innov, gene|
|
|
143
|
+
if gene.enabled?
|
|
144
|
+
raise NeatException.new "Can't find #{gene.out_neuron}" if @neurons[gene.out_neuron].nil?
|
|
145
|
+
@neurons[gene.out_neuron] << @neurons[gene.in_neuron]
|
|
146
|
+
@neural_gene_map[gene.out_neuron] << gene unless gene.in_neuron.nil?
|
|
147
|
+
end
|
|
148
|
+
end unless @genes.nil?
|
|
149
|
+
if @genes.nil?
|
|
150
|
+
$log.error 'Genes Not Present'
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Add new neurons to the fold
|
|
155
|
+
def add_neurons(*neus)
|
|
156
|
+
neus.each do |neu|
|
|
157
|
+
@neurons[neu.name] = neu
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Genes added here MUST correspond to pre-existing neurons.
|
|
162
|
+
# Be sure to do add_neurons first!!!!
|
|
163
|
+
def add_genes(*genes)
|
|
164
|
+
genes.each do |gene|
|
|
165
|
+
raise NeatException.new "Neuron #{gene.in_neuron} missing" unless @neurons.member? gene.in_neuron
|
|
166
|
+
raise NeatException.new "Neuron #{gene.out_neuron} missing" unless @neurons.member? gene.out_neuron
|
|
167
|
+
@genes[gene.innovation] = gene
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# We take the neural hashes (presumably from other neurons), and innervate them.
|
|
172
|
+
# We do this in distinctions based on the neuron's names.
|
|
173
|
+
# FIXME We need to randomly select a neuron in the case of clashes.
|
|
174
|
+
# @param [Hash] hneus -- hashes of neurons to innervate
|
|
175
|
+
def innervate!(*hneus)
|
|
176
|
+
hneus.each do |neus|
|
|
177
|
+
@neurons.merge! neus.dclone
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Go through the list of neurons and drop
|
|
182
|
+
# any neurons not referenced by the genes.
|
|
183
|
+
#
|
|
184
|
+
# Then go through the genes and drop any that
|
|
185
|
+
# are dangling (i.e. no matching neurons)
|
|
186
|
+
#
|
|
187
|
+
# Then make sure that @neural_inputs and @neural_outputs reference the actual
|
|
188
|
+
# instance neurons in @neurons
|
|
189
|
+
#
|
|
190
|
+
# TODO add this circularity check to prune!
|
|
191
|
+
def prune!
|
|
192
|
+
# Take care of dangling neurons
|
|
193
|
+
neunames = @genes.values.map{|g| [g.in_neuron, g.out_neuron]}.flatten.to_set
|
|
194
|
+
@neurons = Hash[@neurons.values.reject do |n|
|
|
195
|
+
not neunames.member? n.name
|
|
196
|
+
end.map do |n|
|
|
197
|
+
[n.name, n]
|
|
198
|
+
end]
|
|
199
|
+
|
|
200
|
+
# Take care of dangling genes
|
|
201
|
+
@genes = Hash[@genes.values.reject do |gene|
|
|
202
|
+
not (@neurons.member?(gene.in_neuron) and @neurons.member?(gene.out_neuron))
|
|
203
|
+
end.map do |gene|
|
|
204
|
+
[gene.name, gene]
|
|
205
|
+
end]
|
|
206
|
+
|
|
207
|
+
# Make sure @neural_inputs and @neural_outputs are consistent
|
|
208
|
+
@neural_inputs = Hash[@neural_inputs.values.map{|n| [n.name, @neurons[n.name]]}]
|
|
209
|
+
@neural_outputs = Hash[@neural_outputs.values.map{|n| [n.name, @neurons[n.name]]}]
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Calculate the cost of this genotype.
|
|
213
|
+
def fitness_cost
|
|
214
|
+
p = @controller.parms
|
|
215
|
+
p.fitness_cost_per_neuron * @neurons.size + p.fitness_cost_per_gene * @genes.size
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def dump_s
|
|
219
|
+
to_s + "\ngenes:\n" + @genes.map{|k, gene|
|
|
220
|
+
gene.to_s}.join("\n") + "\nneurons:\n" + @neurons.map{|k, neu|
|
|
221
|
+
neu.to_s}.join("\n")
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
#= Gene Specification
|
|
225
|
+
# The Gene specifices a singlular input and
|
|
226
|
+
# output neuron, which represents a connection
|
|
227
|
+
# between them, along with the weight of that
|
|
228
|
+
# connection, which may be positive, negative, or zero.
|
|
229
|
+
#
|
|
230
|
+
# There is also the enabled flag
|
|
231
|
+
class Gene < NeatOb
|
|
232
|
+
# parent genotype
|
|
233
|
+
attr_accessor :genotype
|
|
234
|
+
|
|
235
|
+
# innovation number
|
|
236
|
+
attr_accessor :innovation
|
|
237
|
+
|
|
238
|
+
# input neuron's name (where our output goes)
|
|
239
|
+
# ouptut neuron's name (neuron to be queried)
|
|
240
|
+
attr_accessor :in_neuron, :out_neuron
|
|
241
|
+
|
|
242
|
+
# weight of the connection
|
|
243
|
+
attr_accessor :weight
|
|
244
|
+
# Is this gene enabled?
|
|
245
|
+
attr_accessor :enabled
|
|
246
|
+
|
|
247
|
+
def initialize(genotype, &block)
|
|
248
|
+
super genotype.controller
|
|
249
|
+
@genotype = genotype
|
|
250
|
+
@enabled = true
|
|
251
|
+
@innovation = NEAT::new_innovation
|
|
252
|
+
@in_neuron = @out_neuron = nil
|
|
253
|
+
block.(self) unless block.nil?
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def enabled? ; @enabled ; end
|
|
257
|
+
def disabled? ; not enabled? ; end
|
|
258
|
+
|
|
259
|
+
# Create a new Gene and set it up fully.
|
|
260
|
+
## genotype -- genotype
|
|
261
|
+
## input -- name of input neuron connection
|
|
262
|
+
## output -- name of output neuron connection
|
|
263
|
+
## weight -- weight to give neuron (optional)
|
|
264
|
+
## innov -- innovation number of gene (optional)
|
|
265
|
+
def self.[](genotype, input, output, weight = 0.0, innov = nil)
|
|
266
|
+
g = Gene.new genotype
|
|
267
|
+
g.in_neuron = (input.kind_of? Symbol) ? input : input.name
|
|
268
|
+
g.out_neuron = (output.kind_of? Symbol) ? output : output.name
|
|
269
|
+
g.weight = weight
|
|
270
|
+
g.innovation = innov unless innov.nil?
|
|
271
|
+
return g
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def to_s
|
|
275
|
+
super + "[i%s,w%s,%s]" % [@innovation, @weight, self.enabled?]
|
|
276
|
+
end
|
|
277
|
+
alias_method :dump_s, :to_s
|
|
278
|
+
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
#= Phenotype part of the Critter
|
|
284
|
+
# This is created by Evolver.
|
|
285
|
+
class Phenotype < NeatOb
|
|
286
|
+
include Math
|
|
287
|
+
|
|
288
|
+
# Critter to which we belong
|
|
289
|
+
attr_accessor :critter
|
|
290
|
+
|
|
291
|
+
# Expressed code as a string (that was instance_eval()ed)
|
|
292
|
+
attr_accessor :code
|
|
293
|
+
|
|
294
|
+
def self.[](critter)
|
|
295
|
+
ph = Phenotype.new critter.controller
|
|
296
|
+
ph.critter = critter
|
|
297
|
+
ph.code = "## Phenotype Code %s for critter %s\n" % [ph.name, critter.name]
|
|
298
|
+
return ph
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Take what is in code and express that!
|
|
302
|
+
def express!
|
|
303
|
+
instance_eval @code
|
|
304
|
+
self
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# This function is re-written by Expressor -- with parameters and all.
|
|
308
|
+
# It returns a "response" in the form of a response hash.
|
|
309
|
+
# TODO This *is* network activation, so we should rename this at a later date...
|
|
310
|
+
def stimulate
|
|
311
|
+
nil
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# This gives us a complete
|
|
315
|
+
def to_s
|
|
316
|
+
"## %s\n%s" % [super, @code]
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Compare ourselves against another critter for
|
|
321
|
+
# compability.
|
|
322
|
+
#
|
|
323
|
+
# The function to be used here is:
|
|
324
|
+
## distance = c1*E + c2*D + c3*W
|
|
325
|
+
#
|
|
326
|
+
# Where:
|
|
327
|
+
## E, D - The number of excess and disjoint genes repesctively.
|
|
328
|
+
## N - The number of genes in the largest genome.
|
|
329
|
+
## W - The sum of absolute weight differences.
|
|
330
|
+
#
|
|
331
|
+
# This is a variation of the formulation suggested by the Stanley
|
|
332
|
+
# paper, which normalizes the E and D terms by N.
|
|
333
|
+
def compare(oc)
|
|
334
|
+
c1 = @controller.parms.excess_coefficient
|
|
335
|
+
c2 = @controller.parms.disjoint_coefficient
|
|
336
|
+
c3 = @controller.parms.weight_coefficient
|
|
337
|
+
e = excess(oc)
|
|
338
|
+
d = disjoint(oc)
|
|
339
|
+
w = weight_diff(oc)
|
|
340
|
+
return c1 * e + c2 * d + c3 * w
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Critter print
|
|
344
|
+
def dump_s
|
|
345
|
+
to_s + @genotype.dump_s + "\n" + @phenotype.to_s + "\n"
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
private
|
|
349
|
+
# Return a count of excesses.
|
|
350
|
+
def excess(oc)
|
|
351
|
+
(@genotype.genes.size - oc.genotype.genes.size).abs
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Return the count of disjoint genes
|
|
355
|
+
def disjoint(oc)
|
|
356
|
+
a = @genotype.genes.keys
|
|
357
|
+
b = oc.genotype.genes.keys
|
|
358
|
+
(a - b).size + (b - a).size - excess(oc)
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
#
|
|
362
|
+
def weight_diff(oc)
|
|
363
|
+
ag = @genotype.genes
|
|
364
|
+
bg = oc.genotype.genes
|
|
365
|
+
matches = ag.keys & bg.keys
|
|
366
|
+
unless matches.empty?
|
|
367
|
+
matches.map{|i| (ag[i].weight - bg[i].weight).abs}.reduce{|w, ws| w + ws} / matches.size
|
|
368
|
+
else
|
|
369
|
+
0
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
end
|
|
374
|
+
end
|
data/lib/rubyneat/dsl.rb
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
require 'rubyneat/rubyneat'
|
|
2
|
+
|
|
3
|
+
=begin rdoc
|
|
4
|
+
= RubyNEAT DSL
|
|
5
|
+
DSL is a domain-specific language for RubyNEAT to allow you to configure the NEAT engine
|
|
6
|
+
for various evolutionary projects.
|
|
7
|
+
=end
|
|
8
|
+
module NEAT
|
|
9
|
+
module DSL
|
|
10
|
+
include NEAT
|
|
11
|
+
include NEAT::BasicNeuronTypes
|
|
12
|
+
include Math
|
|
13
|
+
|
|
14
|
+
# DSL -- Define defines the parameters to the controller.
|
|
15
|
+
def define(name = NEAT.random_name_generator, &block)
|
|
16
|
+
[
|
|
17
|
+
:inputs,
|
|
18
|
+
:outputs,
|
|
19
|
+
:hidden # we really don't care about mapping hidden neurons, but we'll ignore them later.
|
|
20
|
+
].each do |iometh|
|
|
21
|
+
instance_eval %Q[
|
|
22
|
+
def #{iometh}(nodes = nil, &block)
|
|
23
|
+
neui = unless nodes.nil?
|
|
24
|
+
nodes
|
|
25
|
+
else
|
|
26
|
+
block.()
|
|
27
|
+
end
|
|
28
|
+
NEAT::controller.neural_#{iometh} = if neui.kind_of? Hash
|
|
29
|
+
neui
|
|
30
|
+
else
|
|
31
|
+
Hash[neui.map{|n| [NEAT::random_name_generator, n]}]
|
|
32
|
+
end
|
|
33
|
+
end]
|
|
34
|
+
end
|
|
35
|
+
block.(NEAT::controller)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# DSL -- Run evolution
|
|
39
|
+
def evolve(&block)
|
|
40
|
+
# Query function is called with the sequence (time evolution) number,
|
|
41
|
+
# and returns an array or hash of parameters that will be given
|
|
42
|
+
# to the input nodes. In the case of hash, the keys in the hash
|
|
43
|
+
# shall correspond to the names given to the input neurons.
|
|
44
|
+
def query(&block)
|
|
45
|
+
NEAT::controller.query_func = block
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def recurrence(&block)
|
|
49
|
+
NEAT::controller.recurrence_func = block
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# fitness function calls the block with 2 vectors or two hashes, input and output
|
|
53
|
+
# vectors of the critter being evaluated for fitness, as well as a sequence
|
|
54
|
+
# number that can be used to index what the actual output should be.
|
|
55
|
+
# |vin, vout, seq|
|
|
56
|
+
def fitness(&block)
|
|
57
|
+
NEAT::controller.fitness_func = block
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Fitness ordering -- given 2 fitness numbers,
|
|
61
|
+
# use the <=> to compare them (or the equivalent, following
|
|
62
|
+
# the +1, 0, -1 that is in the sense of <=>)
|
|
63
|
+
def compare(&block)
|
|
64
|
+
NEAT::controller.compare_func = block
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Calculation to add the cost to the fitness, resulting in a fitness
|
|
68
|
+
# that incorporates the cost for sorting purposes.
|
|
69
|
+
def cost(&block)
|
|
70
|
+
NEAT::controller.cost_func = block
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Stop the progression once the fitness criteria is reached
|
|
74
|
+
# for the most fit critter
|
|
75
|
+
def stop_on_fitness(&block)
|
|
76
|
+
NEAT::controller.stop_on_fit_func = block
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Helper function to
|
|
80
|
+
# Condition boolean vectors to be +1 if true, -1 if false (0 if sigmoid)
|
|
81
|
+
def condition_boolean_vector(vec, sig = :tanh)
|
|
82
|
+
vec.map{|b| b ? 1 : ((sig == :sigmoid) ? 0 : -1)}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Helper function to
|
|
86
|
+
# Uncondition boolean vectors to be +1 if true, -1 if false
|
|
87
|
+
# FIXME we need a better discrimination function
|
|
88
|
+
def uncondition_boolean_vector(vec, sig = :tanh)
|
|
89
|
+
vec.map{|o| o > ((sig == :sigmoid) ? 0.5 : 0) ? true : false}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Helper function to do a simple fitness calculation
|
|
93
|
+
# on the basis of the sum of the square of the diffences
|
|
94
|
+
# of the element in the two vectors.
|
|
95
|
+
def simple_fitness_error(v1, v2)
|
|
96
|
+
sqrt v1.zip(v2).map{|a, b| (a - b) ** 2.0}.reduce{|m, c| m + c}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
block.(NEAT::controller)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Report on evaluations
|
|
103
|
+
def report(&block)
|
|
104
|
+
NEAT::controller.report_hook = block
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Run the engine. The block is called on each generation.
|
|
108
|
+
def run_engine(&block)
|
|
109
|
+
NEAT::controller.end_run_func = block
|
|
110
|
+
NEAT::controller.run
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# This is used to handle the details of our DSL.
|
|
114
|
+
def method_missing(m, *args, &block)
|
|
115
|
+
# we want to catch parameters settings here.
|
|
116
|
+
if NEAT::controller.parms.respond_to? (assignment = (m.to_s + '=').to_sym)
|
|
117
|
+
raise NeatException.new("Missing value(s) to %s" % m) if args.empty?
|
|
118
|
+
val = (args.size == 1) ? args[0] : args
|
|
119
|
+
$log.debug { "Caught method %s with parameter of %s" % [assignment, val] }
|
|
120
|
+
NEAT::controller.parms.send(assignment, val)
|
|
121
|
+
else
|
|
122
|
+
super
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# FIXME: This needs to better specified for cases in which there may be multiple
|
|
129
|
+
# Controllers.
|
|
130
|
+
require 'rubyneat/default_neat'
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'rubyneat/rubyneat'
|
|
2
|
+
|
|
3
|
+
module NEAT
|
|
4
|
+
#= Evaluator evaluates phenotype of critter for fitness, novelty, etc.
|
|
5
|
+
# We can have a chain of these evaluators whose outputs are summed, etc.
|
|
6
|
+
class Evaluator < Operator
|
|
7
|
+
|
|
8
|
+
# This is call prior to any sequence evaluation. Here,
|
|
9
|
+
# we clean up persistent tracking information, etc.
|
|
10
|
+
def ready_for_evaluation(pop)
|
|
11
|
+
@crit_hist = {}
|
|
12
|
+
pop.initialize_for_recurrence!
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Evaluate one step of a sequence of evaluations.
|
|
16
|
+
# For time series and realtime ongoing evaluations,
|
|
17
|
+
# @controller.seq_num governs where in the sequence
|
|
18
|
+
# everything is.
|
|
19
|
+
#
|
|
20
|
+
# Returns [vin, vout], where vin is the input vector,
|
|
21
|
+
# and vout in the output vector from the critter.
|
|
22
|
+
# FIXME: this should not really have to deal with an error.
|
|
23
|
+
# FIXME: the error should be handled upstream from here.
|
|
24
|
+
def evaluate!(critter)
|
|
25
|
+
vin = @controller.query_func.(@controller.seq_num)
|
|
26
|
+
@crit_hist[critter] = {} unless @crit_hist.member? critter
|
|
27
|
+
begin
|
|
28
|
+
vout = critter.phenotype.stimulate *vin, &@controller.recurrence_func
|
|
29
|
+
log.debug "Critter #{critter.name}: vin=#{vin}. vout=#{vout}"
|
|
30
|
+
@crit_hist[critter][@controller.seq_num] = [vin, vout]
|
|
31
|
+
rescue Exception => e
|
|
32
|
+
log.error "Exception #{e} on code:\n#{critter.phenotype.code}"
|
|
33
|
+
@crit_hist[critter][@controller.seq_num] = [vin, :error]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Analyze the evaluation and compute a fitness for the given critter.
|
|
38
|
+
# Note that if cost_func is set, we call that to integrate the cost to
|
|
39
|
+
# the fitness average fitness calculated for the fitness vector.
|
|
40
|
+
def analyze_for_fitness!(critter)
|
|
41
|
+
fitvec = @crit_hist[critter].map{|seq, vio| @controller.fitness_func.(vio[0], vio[1], seq) }
|
|
42
|
+
# Average the fitness vector to get a scalar fitness.
|
|
43
|
+
critter.fitness = unless @controller.cost_func.nil?
|
|
44
|
+
@controller.cost_func.(fitvec, critter.genotype.fitness_cost)
|
|
45
|
+
else
|
|
46
|
+
fitvec.reduce {|a,r| a+r} / fitvec.size.to_f + critter.genotype.fitness_cost
|
|
47
|
+
end
|
|
48
|
+
log.debug "Fitness Vector: #{fitvec}, fitness of #{critter.fitness} assigned to #{critter}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|