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.
Files changed (261) hide show
  1. checksums.yaml +7 -0
  2. data/.directory +4 -0
  3. data/.gitignore.orig +20 -0
  4. data/.idea/.name +1 -0
  5. data/.idea/.rakeTasks +7 -0
  6. data/.idea/dictionaries/trader.xml +3 -0
  7. data/.idea/encodings.xml +5 -0
  8. data/.idea/misc.xml +5 -0
  9. data/.idea/modules.xml +9 -0
  10. data/.idea/rubyneat.iml +197 -0
  11. data/.idea/runConfigurations/invpend_neat.xml +26 -0
  12. data/.idea/runConfigurations/sigdebug_neat.xml +24 -0
  13. data/.idea/runConfigurations/xor_neat.xml +26 -0
  14. data/.idea/runConfigurations/xordebug_neat.xml +24 -0
  15. data/.idea/runConfigurations/xorsin_neat.xml +24 -0
  16. data/.idea/scopes/scope_settings.xml +5 -0
  17. data/.idea/vcs.xml +7 -0
  18. data/.idea/workspace.xml +1124 -0
  19. data/.semver +5 -0
  20. data/.yardoc/checksums +11 -0
  21. data/.yardoc/object_types +0 -0
  22. data/.yardoc/objects/root.dat +0 -0
  23. data/.yardoc/proxy_types +0 -0
  24. data/Gemfile +32 -0
  25. data/Gemfile.lock +135 -0
  26. data/Gemfile.lock.orig +147 -0
  27. data/Guardfile +8 -0
  28. data/Rakefile +61 -0
  29. data/bin/neat +83 -0
  30. data/config/application.rb +5 -0
  31. data/doc/ControllerPoint.html +125 -0
  32. data/doc/CuteA.html +286 -0
  33. data/doc/CuteB.html +297 -0
  34. data/doc/DSL.html +883 -0
  35. data/doc/NEAT/BasicNeuronTypes/BiasNeuron.html +518 -0
  36. data/doc/NEAT/BasicNeuronTypes/CosineNeuron.html +274 -0
  37. data/doc/NEAT/BasicNeuronTypes/InputNeuron.html +366 -0
  38. data/doc/NEAT/BasicNeuronTypes/SigmoidNeuron.html +275 -0
  39. data/doc/NEAT/BasicNeuronTypes/SineNeuron.html +274 -0
  40. data/doc/NEAT/BasicNeuronTypes/TanhNeuron.html +274 -0
  41. data/doc/NEAT/BasicNeuronTypes.html +136 -0
  42. data/doc/NEAT/Controller/NeatSettings.html +3985 -0
  43. data/doc/NEAT/Controller.html +2490 -0
  44. data/doc/NEAT/Critter/Genotype/Gene.html +979 -0
  45. data/doc/NEAT/Critter/Genotype.html +1601 -0
  46. data/doc/NEAT/Critter/Phenotype.html +603 -0
  47. data/doc/NEAT/Critter.html +1037 -0
  48. data/doc/NEAT/DSL.html +1255 -0
  49. data/doc/NEAT/Evaluator.html +420 -0
  50. data/doc/NEAT/Evolver/CritterOp.html +551 -0
  51. data/doc/NEAT/Evolver.html +602 -0
  52. data/doc/NEAT/Expressor.html +327 -0
  53. data/doc/NEAT/Graph/DependencyResolver.html +478 -0
  54. data/doc/NEAT/Graph/GraphException.html +123 -0
  55. data/doc/NEAT/Graph.html +402 -0
  56. data/doc/NEAT/NeatException.html +123 -0
  57. data/doc/NEAT/NeatOb.html +567 -0
  58. data/doc/NEAT/Neuron.html +1067 -0
  59. data/doc/NEAT/Operator.html +162 -0
  60. data/doc/NEAT/Population.html +1961 -0
  61. data/doc/NEAT/Trait.html +169 -0
  62. data/doc/NEAT.html +588 -0
  63. data/doc/_index.html +373 -0
  64. data/doc/class_list.html +54 -0
  65. data/doc/css/common.css +1 -0
  66. data/doc/css/full_list.css +57 -0
  67. data/doc/css/style.css +339 -0
  68. data/doc/file_list.html +53 -0
  69. data/doc/frames.html +26 -0
  70. data/doc/index.html +373 -0
  71. data/doc/js/app.js +219 -0
  72. data/doc/js/full_list.js +178 -0
  73. data/doc/js/jquery.js +4 -0
  74. data/doc/method_list.html +1415 -0
  75. data/doc/top-level-namespace.html +164 -0
  76. data/foo/foo_aquarium_example.rb +38 -0
  77. data/foo/foo_gosu.rb +99 -0
  78. data/foo/foo_rubygoo.rb +104 -0
  79. data/foo/foo_sdl.rb +34 -0
  80. data/foo/icon.png +0 -0
  81. data/lib/rubyneat/critter.rb +374 -0
  82. data/lib/rubyneat/default_neat.rb +10 -0
  83. data/lib/rubyneat/dsl.rb +130 -0
  84. data/lib/rubyneat/evaluator.rb +51 -0
  85. data/lib/rubyneat/evolver.rb +315 -0
  86. data/lib/rubyneat/expressor.rb +110 -0
  87. data/lib/rubyneat/graph.rb +95 -0
  88. data/lib/rubyneat/neuron.rb +152 -0
  89. data/lib/rubyneat/population.rb +227 -0
  90. data/lib/rubyneat/rubyneat.rb +429 -0
  91. data/lib/rubyneat.rb +8 -0
  92. data/neater/invpend_neat.rb +150 -0
  93. data/neater/rnlib/inverted_pendulum.rb +380 -0
  94. data/neater/rnlib/xor.rb +10 -0
  95. data/neater/sigdebug_neat.rb +136 -0
  96. data/neater/xor_neat.rb +137 -0
  97. data/neater/xoranalog_neat.rb +138 -0
  98. data/neater/xorsin_neat.rb +143 -0
  99. data/projectFilesBackup/.idea/rubyneat.iml +180 -0
  100. data/public/.directory +4 -0
  101. data/public/background.png +0 -0
  102. data/public/background.xcf +0 -0
  103. data/public/cart.png +0 -0
  104. data/public/cart.xcf +0 -0
  105. data/public/metalpoles_molton_ball_l.jpg +0 -0
  106. data/public/old_background.png +0 -0
  107. data/public/pointer.png +0 -0
  108. data/public/pointer.xcf +0 -0
  109. data/public/pole.kra +0 -0
  110. data/public/pole.png +0 -0
  111. data/public/pole.xcf +0 -0
  112. data/public/wheel-of-year-stone-DD-131-WOYS.jpg +0 -0
  113. data/public/wheel.png +0 -0
  114. data/public/wheel.xcf +0 -0
  115. data/public/wood-planks-texture.jpg +0 -0
  116. data/rdoc/ControllerPoint.html +116 -0
  117. data/rdoc/CuteA.html +177 -0
  118. data/rdoc/CuteB.html +178 -0
  119. data/rdoc/DSLSetup.html +177 -0
  120. data/rdoc/GameTestWindow.html +242 -0
  121. data/rdoc/GameWindow.html +292 -0
  122. data/rdoc/Gemfile.html +215 -0
  123. data/rdoc/Gemfile_lock.html +327 -0
  124. data/rdoc/GraphTest.html +210 -0
  125. data/rdoc/Guardfile.html +198 -0
  126. data/rdoc/InvertedPendulum/Cart.html +668 -0
  127. data/rdoc/InvertedPendulum/DSL.html +259 -0
  128. data/rdoc/InvertedPendulum/InvPendWindow.html +402 -0
  129. data/rdoc/InvertedPendulum.html +198 -0
  130. data/rdoc/Logger.html +98 -0
  131. data/rdoc/NEAT/BasicNeuronTypes/BiasNeuron.html +265 -0
  132. data/rdoc/NEAT/BasicNeuronTypes/CosineNeuron.html +162 -0
  133. data/rdoc/NEAT/BasicNeuronTypes/InputNeuron.html +206 -0
  134. data/rdoc/NEAT/BasicNeuronTypes/SigmoidNeuron.html +162 -0
  135. data/rdoc/NEAT/BasicNeuronTypes/SineNeuron.html +162 -0
  136. data/rdoc/NEAT/BasicNeuronTypes/TanhNeuron.html +161 -0
  137. data/rdoc/NEAT/BasicNeuronTypes.html +107 -0
  138. data/rdoc/NEAT/Controller/NeatSettings.html +880 -0
  139. data/rdoc/NEAT/Controller.html +729 -0
  140. data/rdoc/NEAT/Critter/Genotype/Gene.html +457 -0
  141. data/rdoc/NEAT/Critter/Genotype.html +735 -0
  142. data/rdoc/NEAT/Critter/Phenotype.html +330 -0
  143. data/rdoc/NEAT/Critter.html +489 -0
  144. data/rdoc/NEAT/DSL.html +729 -0
  145. data/rdoc/NEAT/Evaluator.html +256 -0
  146. data/rdoc/NEAT/Evolver/CritterOp.html +349 -0
  147. data/rdoc/NEAT/Evolver.html +891 -0
  148. data/rdoc/NEAT/Expressor.html +402 -0
  149. data/rdoc/NEAT/Graph/DependencyResolver.html +291 -0
  150. data/rdoc/NEAT/Graph/GraphException.html +105 -0
  151. data/rdoc/NEAT/Graph.html +263 -0
  152. data/rdoc/NEAT/NeatException.html +105 -0
  153. data/rdoc/NEAT/NeatOb.html +325 -0
  154. data/rdoc/NEAT/Neuron.html +481 -0
  155. data/rdoc/NEAT/Operator.html +109 -0
  156. data/rdoc/NEAT/Population.html +935 -0
  157. data/rdoc/NEAT/Trait.html +117 -0
  158. data/rdoc/NEAT.html +422 -0
  159. data/rdoc/Object.html +384 -0
  160. data/rdoc/Phi.html +98 -0
  161. data/rdoc/Player.html +383 -0
  162. data/rdoc/Rakefile.html +254 -0
  163. data/rdoc/RubyNEAT/Application.html +105 -0
  164. data/rdoc/RubyNEAT.html +98 -0
  165. data/rdoc/SDL/Event2.html +98 -0
  166. data/rdoc/SDL.html +98 -0
  167. data/rdoc/Vector.html +195 -0
  168. data/rdoc/created.rid +125 -0
  169. data/rdoc/doc/ControllerPoint_html.html +299 -0
  170. data/rdoc/doc/CuteA_html.html +438 -0
  171. data/rdoc/doc/CuteB_html.html +436 -0
  172. data/rdoc/doc/DSL_html.html +992 -0
  173. data/rdoc/doc/NEAT/BasicNeuronTypes/BiasNeuron_html.html +617 -0
  174. data/rdoc/doc/NEAT/BasicNeuronTypes/CosineNeuron_html.html +413 -0
  175. data/rdoc/doc/NEAT/BasicNeuronTypes/InputNeuron_html.html +498 -0
  176. data/rdoc/doc/NEAT/BasicNeuronTypes/SigmoidNeuron_html.html +413 -0
  177. data/rdoc/doc/NEAT/BasicNeuronTypes/SineNeuron_html.html +413 -0
  178. data/rdoc/doc/NEAT/BasicNeuronTypes/TanhNeuron_html.html +412 -0
  179. data/rdoc/doc/NEAT/BasicNeuronTypes_html.html +310 -0
  180. data/rdoc/doc/NEAT/Controller/NeatSettings_html.html +3324 -0
  181. data/rdoc/doc/NEAT/Controller_html.html +2212 -0
  182. data/rdoc/doc/NEAT/Critter/Genotype/Gene_html.html +997 -0
  183. data/rdoc/doc/NEAT/Critter/Genotype_html.html +1556 -0
  184. data/rdoc/doc/NEAT/Critter/Phenotype_html.html +687 -0
  185. data/rdoc/doc/NEAT/Critter_html.html +1037 -0
  186. data/rdoc/doc/NEAT/DSL_html.html +1349 -0
  187. data/rdoc/doc/NEAT/Evaluator_html.html +556 -0
  188. data/rdoc/doc/NEAT/Evolver/CritterOp_html.html +690 -0
  189. data/rdoc/doc/NEAT/Evolver_html.html +677 -0
  190. data/rdoc/doc/NEAT/Expressor_html.html +468 -0
  191. data/rdoc/doc/NEAT/Graph/DependencyResolver_html.html +598 -0
  192. data/rdoc/doc/NEAT/Graph/GraphException_html.html +299 -0
  193. data/rdoc/doc/NEAT/Graph_html.html +527 -0
  194. data/rdoc/doc/NEAT/NeatException_html.html +299 -0
  195. data/rdoc/doc/NEAT/NeatOb_html.html +671 -0
  196. data/rdoc/doc/NEAT/Neuron_html.html +1095 -0
  197. data/rdoc/doc/NEAT/Operator_html.html +337 -0
  198. data/rdoc/doc/NEAT/Population_html.html +1795 -0
  199. data/rdoc/doc/NEAT/Trait_html.html +344 -0
  200. data/rdoc/doc/NEAT_html.html +736 -0
  201. data/rdoc/doc/_index_html.html +559 -0
  202. data/rdoc/doc/class_list_html.html +369 -0
  203. data/rdoc/doc/css/common_css.html +188 -0
  204. data/rdoc/doc/css/full_list_css.html +243 -0
  205. data/rdoc/doc/css/style_css.html +530 -0
  206. data/rdoc/doc/file_list_html.html +240 -0
  207. data/rdoc/doc/frames_html.html +217 -0
  208. data/rdoc/doc/index_html.html +559 -0
  209. data/rdoc/doc/js/app_js.html +423 -0
  210. data/rdoc/doc/js/full_list_js.html +372 -0
  211. data/rdoc/doc/js/jquery_js.html +1536 -0
  212. data/rdoc/doc/method_list_html.html +1375 -0
  213. data/rdoc/doc/top-level-namespace_html.html +317 -0
  214. data/rdoc/fonts/Lato-Light.ttf +0 -0
  215. data/rdoc/fonts/Lato-LightItalic.ttf +0 -0
  216. data/rdoc/fonts/Lato-Regular.ttf +0 -0
  217. data/rdoc/fonts/Lato-RegularItalic.ttf +0 -0
  218. data/rdoc/fonts/SourceCodePro-Bold.ttf +0 -0
  219. data/rdoc/fonts/SourceCodePro-Regular.ttf +0 -0
  220. data/rdoc/fonts.css +167 -0
  221. data/rdoc/images/add.png +0 -0
  222. data/rdoc/images/arrow_up.png +0 -0
  223. data/rdoc/images/brick.png +0 -0
  224. data/rdoc/images/brick_link.png +0 -0
  225. data/rdoc/images/bug.png +0 -0
  226. data/rdoc/images/bullet_black.png +0 -0
  227. data/rdoc/images/bullet_toggle_minus.png +0 -0
  228. data/rdoc/images/bullet_toggle_plus.png +0 -0
  229. data/rdoc/images/date.png +0 -0
  230. data/rdoc/images/delete.png +0 -0
  231. data/rdoc/images/find.png +0 -0
  232. data/rdoc/images/loadingAnimation.gif +0 -0
  233. data/rdoc/images/macFFBgHack.png +0 -0
  234. data/rdoc/images/package.png +0 -0
  235. data/rdoc/images/page_green.png +0 -0
  236. data/rdoc/images/page_white_text.png +0 -0
  237. data/rdoc/images/page_white_width.png +0 -0
  238. data/rdoc/images/plugin.png +0 -0
  239. data/rdoc/images/ruby.png +0 -0
  240. data/rdoc/images/tag_blue.png +0 -0
  241. data/rdoc/images/tag_green.png +0 -0
  242. data/rdoc/images/transparent.png +0 -0
  243. data/rdoc/images/wrench.png +0 -0
  244. data/rdoc/images/wrench_orange.png +0 -0
  245. data/rdoc/images/zoom.png +0 -0
  246. data/rdoc/index.html +282 -0
  247. data/rdoc/js/darkfish.js +140 -0
  248. data/rdoc/js/jquery.js +18 -0
  249. data/rdoc/js/navigation.js +142 -0
  250. data/rdoc/js/search.js +109 -0
  251. data/rdoc/js/search_index.js +1 -0
  252. data/rdoc/js/searcher.js +228 -0
  253. data/rdoc/rdoc.css +580 -0
  254. data/rdoc/rubyneat_gemspec.html +387 -0
  255. data/rdoc/table_of_contents.html +2502 -0
  256. data/rdoc/xordebug_log.html +170598 -0
  257. data/rdoc/xorsin_log.html +22569 -0
  258. data/rubyneat.gemspec +347 -0
  259. data/rubyneat.gemspec.orig +375 -0
  260. data/spec/lib/rubyneat/rubyneat_spec.rb +132 -0
  261. 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
@@ -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