rubyneat 0.3.5.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
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