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,150 @@
|
|
1
|
+
#!/usr/bin/env neat
|
2
|
+
require 'rubyneat/dsl'
|
3
|
+
require 'inverted_pendulum'
|
4
|
+
|
5
|
+
include NEAT::DSL
|
6
|
+
include InvertedPendulum::DSL
|
7
|
+
|
8
|
+
=begin rdoc
|
9
|
+
=Inverted Pendulum
|
10
|
+
=end
|
11
|
+
|
12
|
+
|
13
|
+
invpend do |ipwin|
|
14
|
+
puts "Inverted Pendulum -- use the mouse wheel to bang the cart yourself."
|
15
|
+
|
16
|
+
c = cart do
|
17
|
+
{
|
18
|
+
scale: 0.20,
|
19
|
+
ang: 80,
|
20
|
+
xpos: 600.0,
|
21
|
+
cartmass: 200.0, #kg
|
22
|
+
polemass: 100.10, #kg, knobby end only
|
23
|
+
bang: 10.0, # acceleration on a bang event
|
24
|
+
thrust_decay: 2.0, # thrust decay percentage per second
|
25
|
+
window_pix_width: 1280,
|
26
|
+
naked: true # Naked cart, not attached to a window.
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
show cart: c
|
31
|
+
end
|
32
|
+
|
33
|
+
define "InvPend System" do
|
34
|
+
#-----------------------------------------------------
|
35
|
+
#= Neuron Specifications
|
36
|
+
inputs(
|
37
|
+
in_cart_velocity: InputNeuron,
|
38
|
+
in_cart_position: InputNeuron,
|
39
|
+
in_pole_velocity: InputNeuron,
|
40
|
+
in_pole_angle: InputNeuron,
|
41
|
+
bias: BiasNeuron
|
42
|
+
)
|
43
|
+
|
44
|
+
outputs out_bang_left: TanhNeuron, out_bang_right: TanhNeuron
|
45
|
+
|
46
|
+
hidden tan: TanhNeuron
|
47
|
+
|
48
|
+
#-----------------------------------------------------
|
49
|
+
#= Settings
|
50
|
+
# General
|
51
|
+
hash_on_fitness false
|
52
|
+
start_population_size 30
|
53
|
+
population_size 30
|
54
|
+
max_generations 10000
|
55
|
+
max_population_history 10
|
56
|
+
|
57
|
+
#-----------------------------------------------------
|
58
|
+
#= Evolver probabilities and SDs
|
59
|
+
# Perturbations
|
60
|
+
mutate_perturb_gene_weights_prob 0.10
|
61
|
+
mutate_perturb_gene_weights_sd 0.25
|
62
|
+
|
63
|
+
# Complete Change of weight
|
64
|
+
mutate_change_gene_weights_prob 0.10
|
65
|
+
mutate_change_gene_weights_sd 1.00
|
66
|
+
|
67
|
+
# Adding new neurons and genes
|
68
|
+
mutate_add_neuron_prob 0.05
|
69
|
+
mutate_add_gene_prob 0.20
|
70
|
+
|
71
|
+
# Switching genes on and off
|
72
|
+
mutate_gene_disable_prob 0.01
|
73
|
+
mutate_gene_reenable_prob 0.01
|
74
|
+
|
75
|
+
interspecies_mate_rate 0.03
|
76
|
+
mate_only_prob 0.10 #0.7
|
77
|
+
|
78
|
+
# Mating
|
79
|
+
survival_threshold 0.20 # top % allowed to mate in a species.
|
80
|
+
survival_mininum_per_species 4 # for small populations, we need SOMETHING to go on.
|
81
|
+
|
82
|
+
# Fitness costs
|
83
|
+
fitness_cost_per_neuron 0#.00001
|
84
|
+
fitness_cost_per_gene 0#.00001
|
85
|
+
|
86
|
+
# Speciation
|
87
|
+
compatibility_threshold 2.5
|
88
|
+
disjoint_coefficient 0.6
|
89
|
+
excess_coefficient 0.6
|
90
|
+
weight_coefficient 0.2
|
91
|
+
max_species 20
|
92
|
+
dropoff_age 15
|
93
|
+
smallest_species 5
|
94
|
+
|
95
|
+
# Sequencing
|
96
|
+
start_sequence_at 0
|
97
|
+
end_sequence_at 200
|
98
|
+
end
|
99
|
+
|
100
|
+
evolve do
|
101
|
+
# Each sequence query represents a time instant
|
102
|
+
# for evaluating the inverse pendulum.
|
103
|
+
#
|
104
|
+
# For this, we use a named vector (just a Hash, really),
|
105
|
+
# so we can ensure that the input parameters are lined up with
|
106
|
+
# the appropriate input neurons. When using named vectors,
|
107
|
+
# any non-matches are ignored, allowing you to play around
|
108
|
+
# with changes in input neurons with ease.
|
109
|
+
query { |seq|
|
110
|
+
# TODO: do an interation with the cart and return results.
|
111
|
+
# FIXME: how do we know this is a new sequence? I suppose we can
|
112
|
+
# FIXME: tell that the start number has been hit again.
|
113
|
+
{
|
114
|
+
in_cart_velocity: 0,
|
115
|
+
in_cart_position: 0,
|
116
|
+
in_pole_velocity: 0,
|
117
|
+
in_pole_angle: 0
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
# Compare the fitness of two critters.
|
122
|
+
# We may choose a different ordering here.
|
123
|
+
compare {|f1, f2| f2 <=> f1 }
|
124
|
+
|
125
|
+
# Here we integrate the cost with the fitness.
|
126
|
+
cost { |fitvec, cost|
|
127
|
+
#fit = XOR_STATES - fitvec.reduce {|a,r| a+r} - cost
|
128
|
+
#$log.debug ">>>>>>> fitvec #{fitvec} => #{fit}, cost #{cost}"
|
129
|
+
#fit
|
130
|
+
}
|
131
|
+
|
132
|
+
fitness { |vin, vout, seq|
|
133
|
+
}
|
134
|
+
|
135
|
+
stop_on_fitness {|fitness, c|
|
136
|
+
puts "*** Generation Run #{c.generation_num}, best is #{fitness[:best]} ***\n\n"
|
137
|
+
fitness[:best] >= ALMOST_FIT
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
report do |rept|
|
142
|
+
$log.info "REPORT #{rept.to_yaml}"
|
143
|
+
end
|
144
|
+
|
145
|
+
# The block here is called upon the completion of each generation
|
146
|
+
run_engine do |c|
|
147
|
+
$log.info "******** Run of generation %s completed, history count %d ********" %
|
148
|
+
[c.generation_num, c.population_history.size]
|
149
|
+
end
|
150
|
+
|
@@ -0,0 +1,380 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
=Inverted Pendulum Experiment
|
3
|
+
|
4
|
+
Here we provide a graphic visualization of the inverted pendulum
|
5
|
+
problem, so you can actually SEE the problem being solved by
|
6
|
+
RubyNEAT.
|
7
|
+
|
8
|
+
==Notes
|
9
|
+
===Physical Quantities
|
10
|
+
All physical quantities are in SI units.
|
11
|
+
|
12
|
+
===Vectors
|
13
|
+
* We use 3 dimensional vectors here, even though it's a 2D simulation, for the
|
14
|
+
simple reason that cross products require at least 3 components to a vector.
|
15
|
+
* x and z are taken to be on the horizontal plane, and y is taken to be the vertical.
|
16
|
+
* The sign of the *graphical* y will be reversed of the physical y.
|
17
|
+
=end
|
18
|
+
|
19
|
+
require 'gosu'
|
20
|
+
require 'matrix'
|
21
|
+
|
22
|
+
module InvertedPendulum
|
23
|
+
include Math
|
24
|
+
GC = 6.67384e-11 # m3 kg-1 s-2
|
25
|
+
ADTG = -9.81 # m/s**2, acceleration due to gravity on earth
|
26
|
+
TORAD = PI / 180.0 # converts degrees to radians, deg * TORAD
|
27
|
+
PI2 = 2.0 * PI
|
28
|
+
|
29
|
+
# The following are array indicies of the vector.
|
30
|
+
X = 0 # Horizontal, cart travels in this coordinate.
|
31
|
+
Y = 1 # Vertical, gravitation acts in this coordinate
|
32
|
+
Z = 2 # Horizontal, purely there so the cross products work
|
33
|
+
|
34
|
+
GV = Vector[0, ADTG, 0]
|
35
|
+
ZERO_VECTOR = Vector[0,0,0]
|
36
|
+
|
37
|
+
# ID INPUTS
|
38
|
+
MOUSE_LB = 256
|
39
|
+
MOUSE_RB = 257
|
40
|
+
MOUSE_MB = 258
|
41
|
+
MOUSE_ROLL_BACK = 260
|
42
|
+
MOUSE_ROLL_FOREWARD = 259
|
43
|
+
MOUSE_SIDE_BACK = 268
|
44
|
+
MOUSE_SIDE_FOREWARD = 264
|
45
|
+
|
46
|
+
# We do this for speedier simulations, otherwise Vector is immutable.
|
47
|
+
class ::Vector
|
48
|
+
def []=(i, v)
|
49
|
+
@elements[i] = v
|
50
|
+
end
|
51
|
+
|
52
|
+
# Given that self vector is a basis vector,
|
53
|
+
# compute the component vector for v.
|
54
|
+
def basis(v)
|
55
|
+
self * self.inner_product(v)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class InvPendWindow < Gosu::Window
|
60
|
+
attr_accessor :cart
|
61
|
+
attr_reader :pix_width, :pix_height
|
62
|
+
|
63
|
+
def initialize(width: 1280, height: 1024)
|
64
|
+
super(@pix_width = width, @pix_height = height, false)
|
65
|
+
self.caption = "RubyNEAT Inverted Pendulum Simulation -- use mouse wheel to bang the cart."
|
66
|
+
|
67
|
+
@background = {image: Gosu::Image.new(self, 'public/background.png', true),
|
68
|
+
x: 0,
|
69
|
+
y: 0,
|
70
|
+
scale: 1.0
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def update
|
75
|
+
@cart.update unless @cart.nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
def draw
|
79
|
+
@background[:image].draw(@background[:x],
|
80
|
+
@background[:y],
|
81
|
+
0,
|
82
|
+
@background[:scale],
|
83
|
+
@background[:scale])
|
84
|
+
|
85
|
+
@cart.draw unless cart.nil?
|
86
|
+
end
|
87
|
+
|
88
|
+
def needs_cursor?; true; end
|
89
|
+
|
90
|
+
def button_down(id)
|
91
|
+
@cart.button_down(id) unless @cart.nil?
|
92
|
+
end
|
93
|
+
|
94
|
+
def button_up(id)
|
95
|
+
@cart.button_up(id) unless @cart.nil?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class Cart
|
100
|
+
attr_accessor :pix_meters # pixels per meter
|
101
|
+
attr_accessor :ipwin
|
102
|
+
|
103
|
+
#@param ipwin -- the windowed canvalss this will be shown.
|
104
|
+
#@param scale -- visual scale on the canvas.
|
105
|
+
#@param ang -- initial angle in degrees, 0 being the
|
106
|
+
# positive side of the x axis.
|
107
|
+
#@param xpos -- initial x position of the center of cart, in pixels.
|
108
|
+
#@param cartmass -- in kg. included are the mass of the wheels.
|
109
|
+
# We will not deal with the angular momentum
|
110
|
+
# of the wheels, because that's beyond the scope
|
111
|
+
# of what this is supposed to accomplish.
|
112
|
+
#@param polemass -- in kg. The mass of the pole is assumed to all
|
113
|
+
# reside at a point at
|
114
|
+
# the knobby end.
|
115
|
+
#@param bang -- per mouse event, how much bang (acceleration in m/s) to
|
116
|
+
# deliver to the cart
|
117
|
+
def initialize(ipwin: nil,
|
118
|
+
scale: 0.50,
|
119
|
+
ang: 90.1,
|
120
|
+
xpos: 500.0,
|
121
|
+
ypos: 845.0,
|
122
|
+
cartmass: 200.0,
|
123
|
+
polemass: 100.10,
|
124
|
+
bang: 10.0, # acceleration on a bang event
|
125
|
+
thrust_decay: 2.0, # thrust decay percentage per second
|
126
|
+
window_pix_width: 1280,
|
127
|
+
update_interval: 16.666666,
|
128
|
+
naked: false)
|
129
|
+
@t = 0
|
130
|
+
@bang = bang
|
131
|
+
@thrust = 0 # accumulated bang
|
132
|
+
@thrust_decay = thrust_decay
|
133
|
+
@scale = scale
|
134
|
+
@cart_length = 5.0 # meters
|
135
|
+
@pix_meters = 640.0 * scale / @cart_length
|
136
|
+
@ipwin = ipwin
|
137
|
+
@pix_width = @ipwin.nil? ? window_pix_width : @ipwin.pix_width
|
138
|
+
@update_interval = ipwin.nil? ? update_interval : @ipwin.update_interval
|
139
|
+
@cart = {
|
140
|
+
pos: Vector[xpos / @pix_meters, ypos / @pix_meters, 0],
|
141
|
+
vel: Vector[0.0, 0, 0], #speed in meters per second
|
142
|
+
acc: Vector[0.0, 0, 0], #acceleration in meters per second squared
|
143
|
+
scale: 0.2 * scale,
|
144
|
+
length: nil, # in meters, calculated from the scaled pixel length.
|
145
|
+
height: nil, # in meters, calculated from the scaled pixel height
|
146
|
+
basis: {
|
147
|
+
horiz: Vector[1.0, 0.0, 0.0], # The cart is resticted to movement on the x axis
|
148
|
+
vert: Vector[0.0, 1.0, 0.0]}, # so these basis vectors will never change.
|
149
|
+
force: {},
|
150
|
+
mass: cartmass
|
151
|
+
}
|
152
|
+
|
153
|
+
# Pole is relative to cart, and in the image is laying horizontal.
|
154
|
+
@pole = {
|
155
|
+
z: 0,
|
156
|
+
xoff: 0.0,
|
157
|
+
yoff: 0.5,
|
158
|
+
ang: ang, # angle is in degrees
|
159
|
+
dang: 0.0, # degrees per second
|
160
|
+
ddang: nil, # angular acceleration, degree / second ** 2
|
161
|
+
cdang: 0.0, # degrees per second, contribution from cart
|
162
|
+
cddang: nil, # angular acceleration, degree / second ** 2, contribution from cart
|
163
|
+
scale: 0.2 * scale,
|
164
|
+
force: {}, # shall hold the 2 force vectors :shaft and :radial
|
165
|
+
basis: {}, # basis (unit) vectors
|
166
|
+
cacc: Vector[0,0,0], # acceleration from cart
|
167
|
+
length: nil, # in meters, calculated from the scaled pixel length.
|
168
|
+
mass: polemass
|
169
|
+
}
|
170
|
+
|
171
|
+
# Wheels is relative to cart
|
172
|
+
@wheels = [
|
173
|
+
{
|
174
|
+
ang: 0,
|
175
|
+
dang: 100, #FIXME: delete this for this will be overwritten anyway
|
176
|
+
xoff: -0.7, # percentage from center
|
177
|
+
yoff: 0.4, # percentage from center
|
178
|
+
scale: 0.2 * scale
|
179
|
+
},
|
180
|
+
{
|
181
|
+
ang: 0,
|
182
|
+
dang: 12.33, #FIXME: delete this for this will be overwritten anyway
|
183
|
+
xoff: 0.7,
|
184
|
+
yoff: 0.4,
|
185
|
+
scale: 0.2 * scale
|
186
|
+
}
|
187
|
+
]
|
188
|
+
end
|
189
|
+
|
190
|
+
def ipwin=(ipwin)
|
191
|
+
@ipwin = ipwin
|
192
|
+
@cart[:image] = Gosu::Image.new(ipwin, 'public/cart.png', true)
|
193
|
+
@pole[:image] = Gosu::Image.new(ipwin, 'public/pole.png', true)
|
194
|
+
@wheels.each {|w|
|
195
|
+
w[:image] = Gosu::Image.new(ipwin, 'public/wheel.png', true)
|
196
|
+
# radius of wheel in meters, need this for rotational velocity calculation
|
197
|
+
w[:radius] = w[:image].width * w[:scale] / @pix_meters / 2.0
|
198
|
+
w[:circumference] = w[:radius] * 2.0 * PI
|
199
|
+
}
|
200
|
+
|
201
|
+
@cart[:length] = @cart[:image].width * @cart[:scale] / @pix_meters
|
202
|
+
@cart[:height] = @cart[:image].height * @cart[:scale] / @pix_meters
|
203
|
+
@pole[:length] = @pole[:image].width * @pole[:scale] / @pix_meters
|
204
|
+
end
|
205
|
+
|
206
|
+
# Provide a bang for the cart.
|
207
|
+
# Return a thrust (acc) vector (really just x)
|
208
|
+
# based on thrust
|
209
|
+
#@param bb -- bang factor, normally -1, 0, or 1, but could be
|
210
|
+
# other values based on being a multiplier of the actual bang to be
|
211
|
+
# applied.
|
212
|
+
def big_bang(bb = 0)
|
213
|
+
# FIXME: this is temporary. Eventually this will call a callback in the Neater script.
|
214
|
+
v = Vector[@thrust, 0, 0]
|
215
|
+
@thrust += @bang * bb
|
216
|
+
@thrust -= @thrust * @thrust_decay * @dt
|
217
|
+
v
|
218
|
+
end
|
219
|
+
|
220
|
+
def button_down(id)
|
221
|
+
big_bang case id
|
222
|
+
when MOUSE_ROLL_FOREWARD
|
223
|
+
1.0
|
224
|
+
when MOUSE_ROLL_BACK
|
225
|
+
-1.0
|
226
|
+
else
|
227
|
+
0
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def button_up(id)
|
232
|
+
# no op for now.
|
233
|
+
end
|
234
|
+
|
235
|
+
def update
|
236
|
+
## Physics updates
|
237
|
+
@dt = @update_interval / 1000.0
|
238
|
+
@t += @dt
|
239
|
+
|
240
|
+
## Pole (Pendulum) forces, accelerations, etc.
|
241
|
+
# basis vectors
|
242
|
+
ang = @pole[:ang] * TORAD
|
243
|
+
@pole[:time] = @t
|
244
|
+
@pole[:basis][:shaft] = iShaft = Vector[cos(ang), sin(ang), 0]
|
245
|
+
@pole[:basis][:radial] = iRadial = Vector[sin(ang), -cos(ang), 0]
|
246
|
+
@pole[:r] = r = iShaft * @pole[:length]
|
247
|
+
@pole[:force][:shaft] = iShaft.basis(GV * @pole[:mass])
|
248
|
+
@pole[:force][:radial] = radial = iRadial.basis(GV * @pole[:mass])
|
249
|
+
# the magnitude of the radial vector goes to the torque on
|
250
|
+
@pole[:force][:torque] = torque = r.cross_product(radial)
|
251
|
+
@pole[:alpha] = alpha = torque / (@pole[:mass] * (@pole[:length] ** 2.0))
|
252
|
+
@pole[:ddang] = -alpha[Z] / TORAD # the pseudo vector component Z is the signed magnitude
|
253
|
+
@pole[:dang] += @pole[:ddang] * @dt # angular velocity
|
254
|
+
@pole[:vel] = @pole[:basis][:radial] * (@pole[:dang] * TORAD * PI2 * @pole[:length]) #linear velocity at the pole mass
|
255
|
+
@pole[:ang] += @pole[:dang] * @dt
|
256
|
+
|
257
|
+
## Cart forces from pole [:force][:shaft] and centifugal
|
258
|
+
@cart[:time] = @t
|
259
|
+
|
260
|
+
@pole[:force][:centrifugal] = centrifugal = (@pole[:basis][:shaft] * @pole[:vel].magnitude ** 2) * @pole[:mass]
|
261
|
+
@cart[:force][:shaft] = shaft = @pole[:force][:shaft] # + centrifugal #FIXME cemtrifugal calculations wonky!
|
262
|
+
@cart[:force][:horiz] = horiz = @cart[:basis][:horiz].basis shaft
|
263
|
+
@cart[:force][:vert] = @cart[:basis][:vert].basis shaft # not that we use the vert
|
264
|
+
|
265
|
+
# Now the horz force induces an acceleration on the cart.
|
266
|
+
# Any additions to the acceleration vector must be done after
|
267
|
+
# this point.
|
268
|
+
@cart[:acc] = horiz / (@cart[:mass] + @pole[:mass] * cos(ang).abs)
|
269
|
+
|
270
|
+
@cart[:acc] += big_bang
|
271
|
+
|
272
|
+
## Cart acceleration also affects angular torque
|
273
|
+
# FIXME: Note that recalculations are being done here, which
|
274
|
+
# FIXME: are not DRY. Redo this properly later.
|
275
|
+
@pole[:cacc] = cacc = @cart[:acc] * -1.0
|
276
|
+
@pole[:force][:cradial] = radial = iRadial.basis(cacc * @pole[:mass])
|
277
|
+
# the magnitude of the radial vector goes to the torque on
|
278
|
+
@pole[:force][:ctorque] = torque = r.cross_product(radial)
|
279
|
+
@pole[:calpha] = alpha = torque / (@pole[:mass] * (@pole[:length] ** 2.0))
|
280
|
+
@pole[:cddang] = -alpha[Z] / TORAD # the pseudo vector component Z is the signed magnitude
|
281
|
+
@pole[:cdang] += @pole[:cddang] * @dt
|
282
|
+
@pole[:ang] += @pole[:cdang] * @dt
|
283
|
+
|
284
|
+
## Actual cart physics
|
285
|
+
@cart[:vel][X] += @cart[:acc][X] * @dt
|
286
|
+
@cart[:pos][X] += @cart[:vel][X] * @dt
|
287
|
+
@cart[:x] = @cart[:pos][X] * @pix_meters
|
288
|
+
@cart[:y] = @cart[:pos][Y] * @pix_meters
|
289
|
+
|
290
|
+
#puts '=' * 80
|
291
|
+
#pp({pole: @pole, cart: @cart})
|
292
|
+
|
293
|
+
# wheels physics -- angular velocity of each wheel based
|
294
|
+
# on the linear velocity of the cart.
|
295
|
+
@wheels.each do |w|
|
296
|
+
w[:dang] = 360.0 * @cart[:vel][X] / w[:circumference]
|
297
|
+
w[:ang] += w[:dang] * @dt
|
298
|
+
end
|
299
|
+
|
300
|
+
# model update
|
301
|
+
self.update_cart
|
302
|
+
end
|
303
|
+
|
304
|
+
# update the positions of all the compnents of the cart
|
305
|
+
# in terms of the relative positional relationships
|
306
|
+
def update_cart
|
307
|
+
# center of cart is taken as (x, y), actual is (_x, _y)
|
308
|
+
pw = @cart[:image].width * @cart[:scale]
|
309
|
+
ph = @cart[:image].height * @cart[:scale]
|
310
|
+
@cart[:_x] = @cart[:x] - pw / 2.0
|
311
|
+
@cart[:_y] = @cart[:y] - ph / 2.0
|
312
|
+
|
313
|
+
# Wheels in their respective places
|
314
|
+
@wheels.each do |wl|
|
315
|
+
#ww = wl[:image].width * wl[:scale]
|
316
|
+
#wh = wl[:image].height * wl[:scale]
|
317
|
+
wl[:_x] = @cart[:x] + wl[:xoff] * pw / 2.0
|
318
|
+
wl[:_y] = @cart[:y] + wl[:yoff] * ph / 2.0
|
319
|
+
end
|
320
|
+
|
321
|
+
# Pendulum
|
322
|
+
polew = @pole[:image].width * @pole[:scale]
|
323
|
+
poleh = @pole[:image].height * @pole[:scale]
|
324
|
+
@pole[:_x] = @cart[:x]
|
325
|
+
@pole[:_y] = @cart[:y]
|
326
|
+
end
|
327
|
+
|
328
|
+
def draw
|
329
|
+
@pole[:image].draw_rot( @pole[:_x] % @pix_width,
|
330
|
+
@pole[:_y],
|
331
|
+
@pole[:z],
|
332
|
+
-@pole[:ang], # negative because y is inverted on the canvas
|
333
|
+
@pole[:xoff], @pole[:yoff],
|
334
|
+
@pole[:scale],
|
335
|
+
@pole[:scale])
|
336
|
+
|
337
|
+
@cart[:image].draw(@cart[:_x] % @pix_width,
|
338
|
+
@cart[:_y],
|
339
|
+
0,
|
340
|
+
@cart[:scale],
|
341
|
+
@cart[:scale])
|
342
|
+
|
343
|
+
@wheels.each do |wh|
|
344
|
+
wh[:image].draw_rot(wh[:_x] % @pix_width,
|
345
|
+
wh[:_y],
|
346
|
+
0,
|
347
|
+
wh[:ang],
|
348
|
+
0.5, 0.5,
|
349
|
+
wh[:scale],
|
350
|
+
wh[:scale])
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
module DSL
|
356
|
+
include Math
|
357
|
+
def invpend(&block)
|
358
|
+
@ipwin = InvPendWindow.new
|
359
|
+
|
360
|
+
def cart(&block)
|
361
|
+
@cart_params = block.()
|
362
|
+
unless @cart_params[:naked]
|
363
|
+
@cart = @ipwin.cart = Cart.new({ipwin: @ipwin}.merge @cart_params)
|
364
|
+
else
|
365
|
+
@cart = Cart.new(@cart_params)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
def show(cart: @cart, &block)
|
370
|
+
unless cart.nil?
|
371
|
+
@ipwin.cart = cart
|
372
|
+
cart.ipwin = @ipwin
|
373
|
+
end
|
374
|
+
|
375
|
+
@ipwin.show
|
376
|
+
end
|
377
|
+
block.(@ipwin)
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
data/neater/rnlib/xor.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Basic xor function we shall evolve a net for. Only goes true
|
3
|
+
on one and only one true input, false otherwise.
|
4
|
+
== Note Well
|
5
|
+
This function can either take 0 or -1 to signify "false".
|
6
|
+
=end
|
7
|
+
|
8
|
+
def xor(*inp)
|
9
|
+
inp.map{|n| (n > 0) ? 1 : 0}.reduce {|p, i| p + ((i > 0) ? 1 : 0) } == 1
|
10
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
#!/usr/bin/env neat
|
2
|
+
require 'rubyneat/dsl'
|
3
|
+
require 'xor'
|
4
|
+
|
5
|
+
include NEAT::DSL
|
6
|
+
|
7
|
+
#= DEBUGGING FOR RubyNEAT / Sigmoid
|
8
|
+
|
9
|
+
# The number of inputs to the xor function
|
10
|
+
XOR_INPUTS = 2
|
11
|
+
|
12
|
+
$log.level = Logger::DEBUG
|
13
|
+
|
14
|
+
# This defines the controller
|
15
|
+
define "XOR Sigmoid Debug System" do
|
16
|
+
# Define the IO neurons
|
17
|
+
inputs {
|
18
|
+
cinv = Hash[(1..XOR_INPUTS).map{|i| [("i%s" % i).to_sym, InputNeuron]}]
|
19
|
+
cinv[:bias] = BiasNeuron
|
20
|
+
cinv
|
21
|
+
}
|
22
|
+
outputs out: SigmoidNeuron
|
23
|
+
|
24
|
+
# Hidden neuron specification is optional.
|
25
|
+
# The name given here is largely meaningless, but may be useful as some sort
|
26
|
+
# of unique flag.
|
27
|
+
hidden sig: SigmoidNeuron
|
28
|
+
|
29
|
+
### Settings
|
30
|
+
## General
|
31
|
+
hash_on_fitness = false
|
32
|
+
start_population_size 30
|
33
|
+
population_size 30
|
34
|
+
max_generations 10000
|
35
|
+
max_population_history 10
|
36
|
+
|
37
|
+
## Evolver probabilities and SDs
|
38
|
+
# Perturbations
|
39
|
+
mutate_perturb_gene_weights_prob 0.100
|
40
|
+
mutate_perturb_gene_weights_sd 0.25
|
41
|
+
|
42
|
+
# Complete Change of weight
|
43
|
+
mutate_change_gene_weights_prob 0.001
|
44
|
+
mutate_change_gene_weights_sd 2.00
|
45
|
+
|
46
|
+
# Adding new neurons and genes
|
47
|
+
mutate_add_neuron_prob 0.20
|
48
|
+
mutate_add_gene_prob 0.20
|
49
|
+
|
50
|
+
# Switching genes on and off
|
51
|
+
mutate_gene_disable_prob 0.001
|
52
|
+
mutate_gene_reenable_prob 0.001
|
53
|
+
|
54
|
+
interspecies_mate_rate 0.03
|
55
|
+
mate_only_prob 0.10 #0.7
|
56
|
+
|
57
|
+
# Mating
|
58
|
+
survival_threshold 0.20 # top % allowed to mate in a species.
|
59
|
+
survival_mininum_per_species 4 # for small populations, we need SOMETHING to go on.
|
60
|
+
|
61
|
+
# Fitness costs
|
62
|
+
fitness_cost_per_neuron 0.01
|
63
|
+
fitness_cost_per_gene 0.01
|
64
|
+
|
65
|
+
# Speciation
|
66
|
+
compatibility_threshold 2.5
|
67
|
+
disjoint_coefficient 0.6
|
68
|
+
excess_coefficient 0.6
|
69
|
+
weight_coefficient 0.2
|
70
|
+
max_species 20
|
71
|
+
dropoff_age 15
|
72
|
+
smallest_species 5
|
73
|
+
|
74
|
+
# Sequencing
|
75
|
+
start_sequence_at 0
|
76
|
+
end_sequence_at 2 ** XOR_INPUTS - 1
|
77
|
+
end
|
78
|
+
|
79
|
+
evolve do
|
80
|
+
# This query shall return a vector result that will serve
|
81
|
+
# as the inputs to the critter.
|
82
|
+
query { |seq|
|
83
|
+
# We'll use the seq to create the xor sequences via
|
84
|
+
# the least signficant bits.
|
85
|
+
condition_boolean_vector (0 ... XOR_INPUTS).map{|i| (seq & (1 << i)) != 0}, :sigmoid
|
86
|
+
}
|
87
|
+
|
88
|
+
# Compare the fitness of two critters. We may choose a different ordering
|
89
|
+
# here.
|
90
|
+
compare {|f1, f2| f2 <=> f1 }
|
91
|
+
|
92
|
+
# Here we integrate the cost with the fitness.
|
93
|
+
cost { |fitvec, cost|
|
94
|
+
$log.debug ">>>>>>> fitvec #{fitvec} cost #{cost}"
|
95
|
+
(4 - (fitvec.reduce {|a,r| a+r} / fitvec.size.to_f)) ** 2.0 - cost
|
96
|
+
}
|
97
|
+
|
98
|
+
fitness { |vin, vout, seq|
|
99
|
+
unless vout == :error
|
100
|
+
bin = uncondition_boolean_vector vin, :sigmoid
|
101
|
+
bout = uncondition_boolean_vector vout, :sigmoid
|
102
|
+
bactual = [xor(*vin)]
|
103
|
+
vactual = condition_boolean_vector bactual, :sigmoid
|
104
|
+
fit = (bout == bactual) ? 0.00 : 1.00
|
105
|
+
#simple_fitness_error(vout, vactual.map{|f| f * 0.50 })
|
106
|
+
bfit = (bout == bactual) ? 'T' : 'F'
|
107
|
+
$log.debug "(%s) Fitness bin=%s, bout=%s, bactual=%s, vout=%s, fit=%6.3f, seq=%s" % [bfit,
|
108
|
+
bin,
|
109
|
+
bout,
|
110
|
+
bactual,
|
111
|
+
vout,
|
112
|
+
fit,
|
113
|
+
seq]
|
114
|
+
fit
|
115
|
+
else
|
116
|
+
$log.debug "Error on #{vin} [#{seq}]"
|
117
|
+
1.0
|
118
|
+
end
|
119
|
+
}
|
120
|
+
|
121
|
+
stop_on_fitness {|fitness, c|
|
122
|
+
puts "*** Generation Run #{c.generation_num} ***\n\n"
|
123
|
+
fitness[:best] >= 15
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
report do |rept|
|
128
|
+
$log.info "REPORT #{rept.to_yaml}"
|
129
|
+
exit unless rept[:fitness][:best] < 15.0
|
130
|
+
end
|
131
|
+
|
132
|
+
# The block here is called upon the completion of each generation
|
133
|
+
run_engine do |c|
|
134
|
+
$log.info "******** Run of generation %s completed, history count %d ********" %
|
135
|
+
[c.generation_num, c.population_history.size]
|
136
|
+
end
|