rubyneat 0.3.5.alpha.6 → 0.4.0.alpha.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,10 @@
1
+ require 'semver'
1
2
  require 'distribution'
2
3
  require 'yaml'
3
4
  require 'logger'
4
5
  require 'awesome_print'
5
6
  require 'deep_dive'
7
+ require 'queue_ding'
6
8
 
7
9
  =begin rdoc
8
10
  = RubyNEAT -- a Ruby Implementation of the Neural Evolution of Augmenting Topologies.
@@ -90,9 +92,13 @@ module NEAT
90
92
  #$log.ap ob
91
93
  end
92
94
 
93
- # Basis of all NEAT objects
95
+ # Basis of all NEAT objects.
96
+ # NeatOb has support for NEAT attributes with
97
+ # special support for hooks and queues.
94
98
  class NeatOb
95
99
  include DeepDive
100
+ extend QueueDing
101
+
96
102
  exclude :controller, :name
97
103
 
98
104
  # Designation of this particular object instance
@@ -125,6 +131,134 @@ module NEAT
125
131
  def to_s
126
132
  "%s<%s>" % [self.class, self.name]
127
133
  end
134
+
135
+
136
+ class << self
137
+ # Defaultable attributes of neat attributes.
138
+ #
139
+ # If hooks: true is given, two hook functions are
140
+ # created:
141
+ ## <sym>_add() -- add a hook
142
+ ## <sym>_set() -- set a hook, overwriting all other hooks set or added.
143
+ ## <sym>_clear -- clear all hooks
144
+ ## <sym>_none? -- return true if no hooks are defined.
145
+ ## <sym>_one? -- return true if exactly hook is defined.
146
+ ## <sym>_hook() -- for passing unnamed parameters to a singular hook.
147
+ ## <sym>_np_hook() -- for passing unnamed parameters to a singular hook.
148
+ ## <sym>_hook_itself() -- for getting the proc reference to the hook.
149
+ ## <sym>_hooks() -- for passing unnamed parameters.
150
+ ## <sym>_np_hooks() -- for passing a named parameter list.
151
+ #
152
+ # For *_hook(), the function returns the single result.
153
+ # For *_hooks(), the hook function return an array of results
154
+ # from all the actual registered hooks called.
155
+ def attr_neat(sym,
156
+ default: nil,
157
+ cloneable: nil,
158
+ hooks: false,
159
+ queue: false)
160
+ svar = "@#{sym}"
161
+
162
+ # Guess what clonable should be.
163
+ # This is meant to cover "90%" of the cases.
164
+ cloneable = case
165
+ when default.nil?
166
+ false
167
+ when default.kind_of?(Numeric)
168
+ false
169
+ else
170
+ true
171
+ end if cloneable.nil?
172
+
173
+ # Sanity checks
174
+ raise NeatException("Both hooks and queue cannot both be set for #{sym}.") if hooks and queue
175
+ raise NeatException("Defaults cannot be defined for hooks and queues for #{sym}.") if (hooks or queue) and not default.nil?
176
+
177
+ if hooks
178
+ default = []
179
+ cloneable = true
180
+ hook_setup sym
181
+ end
182
+
183
+ if queue
184
+ default = QDing.new
185
+ cloneable = true
186
+ queue_setup sym
187
+ end
188
+
189
+ define_method("#{sym}=") do |v|
190
+ instance_variable_set(svar, v)
191
+ end unless hooks or queue
192
+
193
+ # TODO: Enhance this getter method for performance.
194
+ define_method(sym) do
195
+ instance_variable_set(svar,
196
+ instance_variable_get(svar) ||
197
+ ((cloneable) ? default.clone
198
+ : default))
199
+ end
200
+ end
201
+
202
+ private
203
+ def hook_setup(sym)
204
+ define_method("#{sym}_add") do |&hook|
205
+ send(sym) << hook
206
+ end
207
+
208
+ define_method("#{sym}_set") do |&hook|
209
+ send(sym).clear
210
+ send(sym) << hook
211
+ end
212
+
213
+ define_method("#{sym}_clear") do
214
+ send(sym).clear
215
+ end
216
+
217
+ define_method("#{sym}_none?") do
218
+ send(sym).empty?
219
+ end
220
+
221
+ define_method("#{sym}_one?") do
222
+ send(sym).size == 1
223
+ end
224
+
225
+ # hooks with named parameters
226
+ define_method("#{sym}_np_hooks") do |**hparams|
227
+ send(sym).map{|funct| funct.(**hparams)}
228
+ end
229
+
230
+ # hooks with traditional parameters
231
+ define_method("#{sym}_hooks") do |*params|
232
+ send(sym).map{|funct| funct.(*params)}
233
+ end
234
+
235
+ # TODO: DRY up the following functions, which does size checking in exacly the same way.
236
+ # Single hook with named parameters
237
+ define_method("#{sym}_np_hook") do |**hparams|
238
+ sz = send(sym).size
239
+ raise NeatException.new("#{sym}_np_hook must have exactly one hook (#{sz})") unless sz == 1
240
+ send(sym).map{|funct| funct.(**hparams)}.first
241
+ end
242
+
243
+ # Single hook with traditional parameters
244
+ define_method("#{sym}_hook") do |*params|
245
+ sz = send(sym).size
246
+ raise NeatException.new("#{sym}_hook must have exactly one hook (#{sz})") unless sz == 1
247
+ send(sym).map{|funct| funct.(*params)}.first
248
+ end
249
+
250
+ # Get the singular hook function
251
+ define_method("#{sym}_hook_itself") do
252
+ sz = send(sym).size
253
+ raise NeatException.new("#{sym}_hook_itself must have exactly one hook (#{sz})") unless sz == 1
254
+ send(sym).first
255
+ end
256
+ end
257
+
258
+ def queue_setup(sym)
259
+ # Add boilerplate code for queues here.
260
+ end
261
+ end
128
262
  end
129
263
 
130
264
  class NeatException < Exception
@@ -161,9 +295,24 @@ module NEAT
161
295
  # a type of "World", if you will, for the entire enterprise.
162
296
  #
163
297
  # Your application shall only have one Controller.
298
+ #
299
+ # FIXME: The function hooks really should be able to take more
300
+ # FIXME: than one hook! we don't need that functionality right
301
+ # FIXME: now. Also, the Controller 'god' object itself will need
302
+ # FIXME: to undergo some refactorization so that we can have many
303
+ # FIXME: of them for HyperNEAT, co-evolution, etc.
304
+ #
305
+ # FIXME: An alternative approach would be to have demigod objects
306
+ # FIXME: where the controller would lord it over them all. Attention
307
+ # FIXME: must also be given to Rubinius and JRuby so that we can
308
+ # FIXME: run under multiple cores.
164
309
  class Controller < NeatOb
310
+ # Version of RubyNEAT runing
311
+ attr_neat :version, default: SemVer.find.format("%M.%m.%p%s")
312
+ attr_neat :neater, default: '--unspecified--'
313
+
165
314
  # global innovation number
166
- attr_reader :glob_innov_num
315
+ attr_neat :glob_innov_num, default: 0, cloneable: false
167
316
 
168
317
  # current sequence number being evaluated
169
318
  attr_reader :seq_num
@@ -193,30 +342,36 @@ module NEAT
193
342
  ## 2 - really verbose
194
343
  ## 3 - maximally verbose
195
344
  # Use in conjunction with log.debug
196
- attr_accessor :verbosity
345
+ attr_neat :verbosity, default: 1
197
346
 
198
347
  # Query function that Critters shall call.
199
- attr_accessor :query_func
348
+ attr_neat :query_func, hooks: true
200
349
 
201
350
  # Fitness function that Critters shall be rated on.
202
- attr_accessor :fitness_func
351
+ attr_neat :fitness_func, hooks: true
203
352
 
204
353
  # Recurrence function that Critters will yield to.
205
- attr_accessor :recurrence_func
354
+ attr_neat :recurrence_func, hooks: true
206
355
 
207
356
  # Compare function for fitness
208
357
  # Cost function for integrating in the cost to the fitness scalar.
209
- attr_accessor :compare_func, :cost_func, :stop_on_fit_func
358
+ attr_neat :compare_func, hooks: true
359
+ attr_neat :cost_func, hooks: true
360
+ attr_neat :stop_on_fit_func, hooks: true
210
361
 
211
362
  # End run function to call at the end of each generational run
212
363
  # Also report_hook to dump reports for the user, etc.
213
- attr_accessor :end_run_func, :report_hook
364
+ attr_neat :end_run, hooks: true
365
+ attr_neat :report, hooks: true
366
+
367
+ # Hook to handle pre_exit functionality
368
+ attr_neat :pre_exit, hooks: true
214
369
 
215
370
  # Logger object for all of RubyNEAT
216
371
  attr_reader :log
217
372
 
218
373
  # Various parameters affecting evolution.
219
- # Based somewhat on the C version of NEAT.
374
+ # Based somewhat on the Ken Stanley C version of NEAT.
220
375
  # TODO not all of these parameters are implemented yet!!!
221
376
  class NeatSettings < NeatOb
222
377
  ## RubyNEAT specific
@@ -255,10 +410,10 @@ module NEAT
255
410
  attr_accessor :mate_singlepoint_prob
256
411
 
257
412
  # Maximum number of generations to run, if given.
258
- attr_accessor :max_generations
413
+ attr_neat :max_generations, default: 1000
259
414
 
260
- # Maximun number of populations to maintain in the history buffer.
261
- attr_accessor :max_population_history
415
+ # Maximum number of populations to maintain in the history buffer.
416
+ attr_neat :max_population_history, default: 10
262
417
 
263
418
  attr_accessor :mutate_add_gene_prob
264
419
  attr_accessor :mutate_add_neuron_prob
@@ -270,9 +425,9 @@ module NEAT
270
425
 
271
426
  # For gene weights perturbations and changes (complete overwrites)
272
427
  attr_accessor :mutate_perturb_gene_weights_prob,
273
- :mutate_perturb_gene_weights_sd,
274
- :mutate_change_gene_weights_prob,
275
- :mutate_change_gene_weights_sd
428
+ :mutate_perturb_gene_weights_sd,
429
+ :mutate_change_gene_weights_prob,
430
+ :mutate_change_gene_weights_sd
276
431
 
277
432
  attr_accessor :mutate_neuron_trait_prob
278
433
  attr_accessor :mutate_only_prob
@@ -284,7 +439,7 @@ module NEAT
284
439
 
285
440
  # fitness costs, if given, use in the computation of fitness
286
441
  # AFTER the overall fitness for the applied stimuli have been
287
- # caclulated.
442
+ # calculated.
288
443
  attr_accessor :fitness_cost_per_neuron
289
444
  attr_accessor :fitness_cost_per_gene
290
445
 
@@ -292,7 +447,8 @@ module NEAT
292
447
  # grow to the bigger population size
293
448
  attr_accessor :start_population_size, :population_size
294
449
 
295
- attr_accessor :start_sequence_at, :end_sequence_at
450
+ attr_neat :start_sequence_at, default: 0
451
+ attr_neat :end_sequence_at, default: 100
296
452
 
297
453
  attr_accessor :print_every
298
454
  attr_accessor :recur_only_prob
@@ -329,10 +485,6 @@ module NEAT
329
485
  # Set up defaults for mandatory entries.
330
486
  def initialize
331
487
  super
332
- @start_sequence_at = 0
333
- @end_sequence_at = 100
334
- @max_generations = 1000
335
-
336
488
  # Default operators
337
489
  @evaluator = Evaluator.new self
338
490
  @expressor = Expressor.new self
@@ -349,8 +501,6 @@ module NEAT
349
501
  parameters: NeatSettings.new,
350
502
  &block)
351
503
  super(self)
352
- @verbosity = 1
353
- @glob_innov_num = 0
354
504
  @gaussian = Distribution::Normal.rng
355
505
  @population_history = []
356
506
  @evolver = Evolver.new self
@@ -376,19 +526,20 @@ module NEAT
376
526
  block.(self) unless block.nil?
377
527
  end
378
528
 
379
- def new_innovation ; @glob_innov_num += 1; end
529
+ def new_innovation ; self.glob_innov_num += 1 ; end
380
530
  def gaussian ; @gaussian.() ; end
381
531
 
382
532
  # Run this evolution.
383
533
  def run
384
534
  pre_run_initialize
385
535
  (1..@parms.max_generations).each do |gen_number|
386
- @generation_num = gen_number
536
+ @generation_num = gen_number # must be set first
387
537
  @population_history << unless @population.nil?
388
538
  @population
389
539
  else
390
540
  @population = @population_class.new(self)
391
541
  end
542
+ @population.generation = gen_number
392
543
  @population_history.shift unless @population_history.size <= @parms.max_population_history
393
544
  @population.mutate!
394
545
  @population.express!
@@ -403,22 +554,22 @@ module NEAT
403
554
  @population.analyze!
404
555
  @population.speciate!
405
556
 
406
- $log.debug @population.dump_s unless @verbosity < 3
557
+ $log.debug @population.dump_s unless self.verbosity < 3
407
558
 
408
559
  new_pop = @population.evolve
409
560
 
410
561
  ## Report hook for evaluation
411
- @report_hook.(@population.report) unless @report_hook.nil?
562
+ report_hooks(@population.report)
412
563
 
413
564
  ## Exit if fitness criteria is reached
414
565
  #FIXME handle this exit condition better!!!!!
415
- exit if @stop_on_fit_func.(@population.report[:fitness], self) unless @stop_on_fit_func.nil?
566
+ exit_neat if stop_on_fit_func_hook(@population.report.last[:fitness], self) unless stop_on_fit_func_none?
416
567
 
417
568
  ## Evolve population
418
569
  @population = new_pop
419
570
 
420
571
  ## Finish up this run
421
- @end_run_func.(self) unless @end_run_func.nil?
572
+ end_run_hooks(self)
422
573
  end
423
574
  end
424
575
 
@@ -428,6 +579,13 @@ module NEAT
428
579
  @evaluator = @evaluator_class.new(self) if @evaluator.nil?
429
580
  @evolver = @evolver_class.new(self) if @evolver.nil?
430
581
  end
582
+
583
+ # Allow us to hook in pre-exit functionality here
584
+ # This function shall never return.
585
+ def exit_neat
586
+ pre_exit_hook(self) unless pre_exit_none?
587
+ exit
588
+ end
431
589
  end
432
590
 
433
591
  @controller = Controller.new
@@ -437,5 +595,5 @@ module NEAT
437
595
  end
438
596
 
439
597
  # We put all the internal requires at the end to avoid conflicts.
440
- require 'rubyneat/neuron'
441
- require 'rubyneat/population'
598
+ require_relative 'neuron'
599
+ require_relative 'population'
data/rubyneat.gemspec CHANGED
@@ -2,35 +2,41 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: rubyneat 0.3.5.alpha.6 ruby lib
5
+ # stub: rubyneat 0.4.0.alpha.3 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "rubyneat"
9
- s.version = "0.3.5.alpha.6"
9
+ s.version = "0.4.0.alpha.3"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Fred Mitchell"]
14
- s.date = "2014-04-24"
14
+ s.date = "2014-08-03"
15
15
  s.description = "\n RubyNEAT -- Neural Evolution of Augmenting Topologies for Ruby.\n By way of an enhanced form of Genetic Algorithms -- the NEAT algorithm,\n populations of neural nets are evolved to handle pre-defined goals.\n\n RubyNEAT is the first implementation of the NEAT algorithm for Ruby, and\n it leverages Ruby's power to implement the NEAT algorithm in a way that would\n be difficult to do in other languages. The 'activation function' is largely\n standalone. Basically, activation is achieved by functional programming.\n\n Meaning, once your network is evolved, you can extract it as source code you\n can then utilize without the RubyNEAT engine.\n\n RubyNEAT can be used for nearly any Machine Learning task you can dream of,\n because it's also extensible and modular. See http://rubyneat.com for the\n details.\n "
16
- s.email = "fred@lrcsoft.com"
16
+ s.email = "lordalveric@yahoo.com"
17
17
  s.executables = ["neat"]
18
18
  s.extra_rdoc_files = [
19
19
  "README.md"
20
20
  ]
21
21
  s.files = [
22
22
  ".directory",
23
+ ".irbrc",
24
+ ".ruby-version",
23
25
  ".semver",
24
26
  "Gemfile",
25
27
  "Gemfile.lock",
28
+ "Gemfile.lock.orig",
29
+ "Gemfile.orig",
26
30
  "README.md",
27
31
  "Rakefile",
28
32
  "bin/neat",
29
33
  "config/application.rb",
30
34
  "lib/rubyneat.rb",
31
35
  "lib/rubyneat/cli.rb",
36
+ "lib/rubyneat/cli/console.rb",
32
37
  "lib/rubyneat/cli/generate.rb",
33
38
  "lib/rubyneat/cli/main.rb",
39
+ "lib/rubyneat/cli/templates/generate/.irbrc.tt",
34
40
  "lib/rubyneat/cli/templates/generate/Gemfile.tt",
35
41
  "lib/rubyneat/cli/templates/generate/README.md.tt",
36
42
  "lib/rubyneat/cli/templates/generate/bin/neat.tt",
@@ -45,12 +51,14 @@ Gem::Specification.new do |s|
45
51
  "lib/rubyneat/graph.rb",
46
52
  "lib/rubyneat/neuron.rb",
47
53
  "lib/rubyneat/population.rb",
54
+ "lib/rubyneat/reporting.rb",
48
55
  "lib/rubyneat/rubyneat.rb",
49
56
  "rubyneat.gemspec",
50
57
  "spec/lib/rubyneat/rubyneat_spec.rb"
51
58
  ]
52
59
  s.homepage = "http://rubyneat.com"
53
60
  s.licenses = ["MIT"]
61
+ s.required_ruby_version = Gem::Requirement.new(">= 2.0")
54
62
  s.rubygems_version = "2.2.2"
55
63
  s.summary = "RubyNEAT NeuralEvolution of Augmenting Topologies"
56
64
 
@@ -65,6 +73,9 @@ Gem::Specification.new do |s|
65
73
  s.add_runtime_dependency(%q<thor>, ["~> 0"])
66
74
  s.add_runtime_dependency(%q<awesome_print>, ["~> 1"])
67
75
  s.add_runtime_dependency(%q<deep_dive>, ["~> 0"])
76
+ s.add_runtime_dependency(%q<bond>, ["~> 0.5"])
77
+ s.add_runtime_dependency(%q<rb-readline>, ["~> 0.5"])
78
+ s.add_runtime_dependency(%q<queue_ding>, [">= 0"])
68
79
  s.add_runtime_dependency(%q<gosu>, ["~> 0"])
69
80
  s.add_runtime_dependency(%q<rubyvis>, ["~> 0"])
70
81
  s.add_development_dependency(%q<rspec>, ["~> 2"])
@@ -82,6 +93,9 @@ Gem::Specification.new do |s|
82
93
  s.add_dependency(%q<thor>, ["~> 0"])
83
94
  s.add_dependency(%q<awesome_print>, ["~> 1"])
84
95
  s.add_dependency(%q<deep_dive>, ["~> 0"])
96
+ s.add_dependency(%q<bond>, ["~> 0.5"])
97
+ s.add_dependency(%q<rb-readline>, ["~> 0.5"])
98
+ s.add_dependency(%q<queue_ding>, [">= 0"])
85
99
  s.add_dependency(%q<gosu>, ["~> 0"])
86
100
  s.add_dependency(%q<rubyvis>, ["~> 0"])
87
101
  s.add_dependency(%q<rspec>, ["~> 2"])
@@ -100,6 +114,9 @@ Gem::Specification.new do |s|
100
114
  s.add_dependency(%q<thor>, ["~> 0"])
101
115
  s.add_dependency(%q<awesome_print>, ["~> 1"])
102
116
  s.add_dependency(%q<deep_dive>, ["~> 0"])
117
+ s.add_dependency(%q<bond>, ["~> 0.5"])
118
+ s.add_dependency(%q<rb-readline>, ["~> 0.5"])
119
+ s.add_dependency(%q<queue_ding>, [">= 0"])
103
120
  s.add_dependency(%q<gosu>, ["~> 0"])
104
121
  s.add_dependency(%q<rubyvis>, ["~> 0"])
105
122
  s.add_dependency(%q<rspec>, ["~> 2"])