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.
data/Rakefile CHANGED
@@ -25,6 +25,7 @@ Jeweler::Tasks.new do |gem|
25
25
  gem.license = "MIT"
26
26
  gem.summary = %Q{RubyNEAT NeuralEvolution of Augmenting Topologies}
27
27
  gem.version = s_version
28
+ gem.required_ruby_version = '>= 2.0'
28
29
  gem.description = %Q{
29
30
  RubyNEAT -- Neural Evolution of Augmenting Topologies for Ruby.
30
31
  By way of an enhanced form of Genetic Algorithms -- the NEAT algorithm,
@@ -42,15 +43,15 @@ Jeweler::Tasks.new do |gem|
42
43
  because it's also extensible and modular. See http://rubyneat.com for the
43
44
  details.
44
45
  }
45
- gem.email = "fred@lrcsoft.com"
46
+ gem.email = "lordalveric@yahoo.com"
46
47
  gem.authors = ["Fred Mitchell"]
47
48
  # dependencies defined in Gemfile
48
49
 
49
50
  # Exclude the Neural Docs directory
50
51
  gem.files.exclude 'Neural_Docs/*', 'foo/**/*', 'rdoc/*',
51
52
  '.idea/**/*', '.idea/**/.*', '.yardoc/**/*',
52
- 'public/**/*', 'neater/**/*', 'doc/**/*',
53
- 'public/**/.*', 'Guardfile'
53
+ 'app/**/*', 'neater/**/*', 'doc/**/*',
54
+ 'app/**/.*', 'Guardfile'
54
55
  end
55
56
  Jeweler::RubygemsDotOrgTasks.new
56
57
 
data/lib/rubyneat.rb CHANGED
@@ -2,7 +2,8 @@
2
2
  =RubyNEAT -- Neural Evolution of Augmenting Topologies
3
3
  =end
4
4
 
5
- require 'rubyneat/rubyneat'
6
- require 'rubyneat/graph'
7
- require 'rubyneat/dsl'
5
+ require_relative 'rubyneat/rubyneat'
6
+ require_relative 'rubyneat/graph'
7
+ require_relative 'rubyneat/dsl'
8
+ require_relative 'rubyneat/reporting'
8
9
  require 'set'
data/lib/rubyneat/cli.rb CHANGED
@@ -4,5 +4,7 @@ require 'semver'
4
4
  NEATER = File.join [Dir.pwd, "neater"]
5
5
  NEATGLOB = NEATER + '/*_neat.rb'
6
6
 
7
- require 'rubyneat/cli/main'
7
+ require_relative 'cli/generate'
8
+ require_relative 'cli/console'
9
+ require_relative 'cli/main'
8
10
  require 'rubyneat'
@@ -0,0 +1,18 @@
1
+ require 'irb'
2
+ require 'irb/completion'
3
+
4
+ module RubyNEAT
5
+ module Cli
6
+ class Console < Thor
7
+ class << self
8
+ def default_command
9
+ # TODO: maybe dynamically set it in Rakefile and then retrieve from ENV?
10
+ # require "#{$calling_gem_name}"
11
+ ARGV.clear
12
+ IRB.start
13
+ end
14
+ alias_method :default_task, :default_command
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,4 +1,4 @@
1
- require 'rubyneat/cli/generate'
1
+ require 'rubyneat/cli'
2
2
 
3
3
  module RubyNEAT
4
4
  module Cli
@@ -43,10 +43,7 @@ module RubyNEAT
43
43
  end
44
44
 
45
45
  desc 'console', 'Run RubyNEAT interactively'
46
- def console
47
- #TODO: Finish the console
48
- puts "Not Implemented Yet."
49
- end
46
+ subcommand 'console', Console
50
47
 
51
48
  desc 'run <neater> [<neater> <neater> ...] [OPTS]', 'Run a Neater'
52
49
  option :log, type: :string, banner: 'info|warn|debug|error'
@@ -57,6 +54,7 @@ module RubyNEAT
57
54
  neaters.map do |neater|
58
55
  "#{neater}_neat.rb"
59
56
  end.each do |file|
57
+ NEAT::controller.neater = file
60
58
  load file
61
59
  end
62
60
  end
@@ -0,0 +1,3 @@
1
+ require 'bond'
2
+ Bond.start
3
+ # TODO: incorporate this in the generator
@@ -222,7 +222,7 @@ module NEAT
222
222
  end
223
223
 
224
224
  #= Gene Specification
225
- # The Gene specifices a singlular input and
225
+ # The Gene specifies a singular input and
226
226
  # output neuron, which represents a connection
227
227
  # between them, along with the weight of that
228
228
  # connection, which may be positive, negative, or zero.
@@ -236,7 +236,7 @@ module NEAT
236
236
  attr_accessor :innovation
237
237
 
238
238
  # input neuron's name (where our output goes)
239
- # ouptut neuron's name (neuron to be queried)
239
+ # output neuron's name (neuron to be queried)
240
240
  attr_accessor :in_neuron, :out_neuron
241
241
 
242
242
  # weight of the connection
@@ -1,4 +1,4 @@
1
- require 'rubyneat/dsl'
1
+ require_relative 'dsl'
2
2
  =begin rdoc
3
3
  = Default Parameters for RubyNEAT
4
4
  Here all default parameters are defined.
data/lib/rubyneat/dsl.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'rubyneat/rubyneat'
1
+ require_relative 'rubyneat'
2
2
 
3
3
  =begin rdoc
4
4
  = RubyNEAT DSL
@@ -42,11 +42,11 @@ module NEAT
42
42
  # to the input nodes. In the case of hash, the keys in the hash
43
43
  # shall correspond to the names given to the input neurons.
44
44
  def query(&block)
45
- NEAT::controller.query_func = block
45
+ NEAT::controller.query_func_add &block
46
46
  end
47
47
 
48
48
  def recurrence(&block)
49
- NEAT::controller.recurrence_func = block
49
+ NEAT::controller.recurrence_func_set &block
50
50
  end
51
51
 
52
52
  # fitness function calls the block with 2 vectors or two hashes, input and output
@@ -54,26 +54,27 @@ module NEAT
54
54
  # number that can be used to index what the actual output should be.
55
55
  # |vin, vout, seq|
56
56
  def fitness(&block)
57
- NEAT::controller.fitness_func = block
57
+ NEAT::controller.fitness_func_set &block
58
58
  end
59
59
 
60
60
  # Fitness ordering -- given 2 fitness numbers,
61
61
  # use the <=> to compare them (or the equivalent, following
62
62
  # the +1, 0, -1 that is in the sense of <=>)
63
63
  def compare(&block)
64
- NEAT::controller.compare_func = block
64
+ NEAT::controller.compare_func_set &block
65
65
  end
66
66
 
67
67
  # Calculation to add the cost to the fitness, resulting in a fitness
68
68
  # that incorporates the cost for sorting purposes.
69
69
  def cost(&block)
70
- NEAT::controller.cost_func = block
70
+ NEAT::controller.cost_func_set &block
71
71
  end
72
72
 
73
73
  # Stop the progression once the fitness criteria is reached
74
- # for the most fit critter
74
+ # for the most fit critter. We allow more than one stop
75
+ # function here.
75
76
  def stop_on_fitness(&block)
76
- NEAT::controller.stop_on_fit_func = block
77
+ NEAT::controller.stop_on_fit_func_add &block
77
78
  end
78
79
 
79
80
  # Helper function to
@@ -101,12 +102,12 @@ module NEAT
101
102
 
102
103
  # Report on evaluations
103
104
  def report(&block)
104
- NEAT::controller.report_hook = block
105
+ NEAT::controller.report_add &block
105
106
  end
106
107
 
107
108
  # Run the engine. The block is called on each generation.
108
109
  def run_engine(&block)
109
- NEAT::controller.end_run_func = block
110
+ NEAT::controller.end_run_add &block
110
111
  NEAT::controller.run
111
112
  end
112
113
 
@@ -125,6 +126,6 @@ module NEAT
125
126
  end
126
127
  end
127
128
 
128
- # FIXME: This needs to better specified for cases in which there may be multiple
129
- # Controllers.
129
+ # FIXME: This needs to better specified for cases in which
130
+ # FIXME: there may be multiple Controllers.
130
131
  require 'rubyneat/default_neat'
@@ -1,4 +1,4 @@
1
- require 'rubyneat/rubyneat'
1
+ require_relative 'rubyneat'
2
2
 
3
3
  module NEAT
4
4
  #= Evaluator evaluates phenotype of critter for fitness, novelty, etc.
@@ -22,10 +22,14 @@ module NEAT
22
22
  # FIXME: this should not really have to deal with an error.
23
23
  # FIXME: the error should be handled upstream from here.
24
24
  def evaluate!(critter)
25
- vin = @controller.query_func.(@controller.seq_num)
25
+ vin = @controller.query_func_hook(@controller.seq_num)
26
26
  @crit_hist[critter] = {} unless @crit_hist.member? critter
27
27
  begin
28
- vout = critter.phenotype.stimulate *vin, &@controller.recurrence_func
28
+ vout = unless @controller.recurrence_func_none?
29
+ critter.phenotype.stimulate *vin, &@controller.recurrence_func_hook_itself
30
+ else
31
+ critter.phenotype.stimulate *vin
32
+ end
29
33
  log.debug "Critter #{critter.name}: vin=#{vin}. vout=#{vout}"
30
34
  @crit_hist[critter][@controller.seq_num] = [vin, vout]
31
35
  rescue Exception => e
@@ -38,10 +42,10 @@ module NEAT
38
42
  # Note that if cost_func is set, we call that to integrate the cost to
39
43
  # the fitness average fitness calculated for the fitness vector.
40
44
  def analyze_for_fitness!(critter)
41
- fitvec = @crit_hist[critter].map{|seq, vio| @controller.fitness_func.(vio[0], vio[1], seq) }
45
+ fitvec = @crit_hist[critter].map{|seq, vio| @controller.fitness_func_hook(vio[0], vio[1], seq) }
42
46
  # Average the fitness vector to get a scalar fitness.
43
- critter.fitness = unless @controller.cost_func.nil?
44
- @controller.cost_func.(fitvec, critter.genotype.fitness_cost)
47
+ critter.fitness = unless @controller.cost_func_none?
48
+ @controller.cost_func_hook(fitvec, critter.genotype.fitness_cost)
45
49
  else
46
50
  fitvec.reduce {|a,r| a+r} / fitvec.size.to_f + critter.genotype.fitness_cost
47
51
  end
@@ -1,5 +1,6 @@
1
1
  require 'rubyneat'
2
2
  require 'distribution'
3
+
3
4
  module NEAT
4
5
  #= Evolver -- Basis of all evolvers.
5
6
  # All evolvers shall derive from this basic evolver (or this one can be
@@ -83,7 +84,7 @@ module NEAT
83
84
  @npop.species.each do |k, sp|
84
85
  sp.sort!{|c1, c2|
85
86
  unless @controller.compare_func.nil?
86
- @controller.compare_func.(c1.fitness, c2.fitness)
87
+ @controller.compare_func_hook(c1.fitness, c2.fitness)
87
88
  else
88
89
  c1.fitness <=> c2.fitness
89
90
  end
@@ -1,4 +1,4 @@
1
- require 'rubyneat/rubyneat'
1
+ require_relative 'rubyneat'
2
2
  require 'pp'
3
3
 
4
4
  module NEAT
@@ -1,5 +1,5 @@
1
1
  require 'rubyneat'
2
- require 'rubyneat/graph'
2
+ require_relative 'graph'
3
3
 
4
4
  =begin rdoc
5
5
  = Neuron Types
@@ -1,4 +1,4 @@
1
- require 'rubyneat/rubyneat'
1
+ require_relative 'rubyneat'
2
2
 
3
3
  module NEAT
4
4
  #= Population of NEAT Critters.
@@ -8,6 +8,11 @@ module NEAT
8
8
  # expression, all the phenotypes shall be created individually.
9
9
  #
10
10
  class Population < NeatOb
11
+ # Generation number of the Population.
12
+ # Any newly-derivied population is always one greater
13
+ # than the former. Needs to be set, invalid if nil.
14
+ attr_neat :generation, default: nil
15
+
11
16
  # Ordered list or hash of input neuron classes
12
17
  # (all critters generated here shall have this)
13
18
  attr_accessor :input_neurons
@@ -46,6 +51,7 @@ module NEAT
46
51
  @critters = (0 ... c.parms.start_population_size || c.parms.population_size).map do
47
52
  Critter.new(self)
48
53
  end
54
+
49
55
  block.(self) unless block.nil?
50
56
  end
51
57
 
@@ -160,22 +166,12 @@ module NEAT
160
166
 
161
167
  end
162
168
 
163
- #== Generate a report on the state of this population.
164
- #
165
- def report
166
- {
167
- fitness: report_fitness,
168
- fitness_species: report_fitness_species,
169
- best_critter: report_best_fit,
170
- worst_critter: report_worst_fit,
171
- }
172
- end
173
-
174
169
  # The "best critter" is the critter with the lowest (closet to zero)
175
170
  # fitness rating.
171
+ # TODO: DRY up best_critter and worst_critter
176
172
  def best_critter
177
- unless @controller.compare_func.nil?
178
- @critters.min {|a, b| @controller.compare_func.(a.fitness, b.fitness) }
173
+ unless @controller.compare_func.empty?
174
+ @critters.min {|a, b| @controller.compare_func_hook(a.fitness, b.fitness) }
179
175
  else
180
176
  @critters.min {|a, b| a.fitness <=> b.fitness}
181
177
  end
@@ -184,8 +180,8 @@ module NEAT
184
180
  # The "worst critter" is the critter with the highest (away from zero)
185
181
  # fitness rating.
186
182
  def worst_critter
187
- unless @controller.compare_func.nil?
188
- @critters.max {|a, b| @controller.compare_func.(a.fitness, b.fitness) }
183
+ unless @controller.compare_func.empty?
184
+ @critters.max {|a, b| @controller.compare_func_hook(a.fitness, b.fitness) }
189
185
  else
190
186
  @critters.max {|a, b| a.fitness <=> b.fitness}
191
187
  end
@@ -194,34 +190,5 @@ module NEAT
194
190
  def dump_s
195
191
  to_s + "\npopulation:\n" + @critters.map{|crit| crit.dump_s }.join("\n")
196
192
  end
197
-
198
- protected
199
- # report on many fitness metrics
200
- def report_fitness
201
- {
202
- overall: @critters.map{|critter| critter.fitness}.reduce{|m, f| m + f} / @critters.size,
203
- best: best_critter.fitness,
204
- worst: worst_critter.fitness,
205
- }
206
- end
207
-
208
- # report on the best and worst species
209
- def report_fitness_species
210
- {
211
- best: nil,
212
- worst: nil,
213
- }
214
- end
215
-
216
- # Find the best fit critter
217
- def report_best_fit
218
- best_critter.phenotype.code
219
- end
220
-
221
- # Find the worst fit critter
222
- def report_worst_fit
223
- worst_critter.phenotype.code
224
- end
225
-
226
193
  end
227
194
  end
@@ -0,0 +1,113 @@
1
+ =begin rdoc
2
+ =RubyNEAT Reporting
3
+ Here we factor out all reporting-related functionality across the entire
4
+ RubyNEAT system to this one place, because reporting is not directly related
5
+ to RubyNEAT functionality. As such, it will make it much easier for forkers
6
+ to slim down RubyNEAT for some specific application where reporting may not
7
+ be so needed.
8
+
9
+ As far as plugins go, we could insist that all plugins do their own reporting.
10
+ However, we wish to insulate such activities from the internal structures of
11
+ the Population, Critters, etc. simply they are subject to change. This affords
12
+ us the one place to look to update the API in response to deep structural changes.
13
+ =end
14
+
15
+ module NEAT
16
+ #= Population Reporting
17
+ # The tangenial reporting needs for the Population module
18
+ # are extracted here simply because they do not directly relate
19
+ # to the operations of the Population.
20
+ class Population
21
+ # report on many fitness metrics
22
+ def report_fitness
23
+ {
24
+ overall: critters.map{|critter| critter.fitness}.reduce{|m, f| m + f} / critters.size,
25
+ best: best_critter.fitness,
26
+ worst: worst_critter.fitness,
27
+ }
28
+ end
29
+
30
+ # report on the best and worst species
31
+ def report_fitness_species
32
+ {
33
+ best: nil,
34
+ worst: nil,
35
+ }
36
+ end
37
+
38
+ # Find the best fit critter
39
+ def report_best_fit
40
+ best_critter.phenotype.code
41
+ end
42
+
43
+ # Find the worst fit critter
44
+ def report_worst_fit
45
+ worst_critter.phenotype.code
46
+ end
47
+
48
+ # Create a hash of critter names and fitness values
49
+ def report_critters
50
+ critters.inject({}){|memo, critter| memo[critter.name] = critter.fitness; memo }
51
+ end
52
+
53
+ #== Generate a report on the state of this population.
54
+ #
55
+ def report
56
+ [
57
+ self,
58
+ {
59
+ generation: generation,
60
+ fitness: report_fitness,
61
+ fitness_species: report_fitness_species,
62
+ best_critter: report_best_fit,
63
+ worst_critter: report_worst_fit,
64
+ all_critters: report_critters,
65
+ }
66
+ ]
67
+ end
68
+
69
+ #TODO: we should probably provide a means to invalidate this cache.
70
+ #TODO: but in most reasonable use cases this would not be called until
71
+ #TODO: after all the critters have been created.
72
+ def critter_hash
73
+ @critter_hash ||= critters.inject({}){|memo, crit| memo[crit.name]=crit; memo}
74
+ end
75
+
76
+ # Retrive list of critters given from parameters given, names of critters.
77
+ # Return the results in an array. Names given must exist. Can be either
78
+ # strings or symbols or something that can be reduced to symbols, at least.
79
+ def find_critters(*names)
80
+ names.map{|name| critter_hash[name.to_sym]}
81
+ end
82
+ end
83
+
84
+ #= Critter Reporting
85
+ # The reporting functionality for critters are represented here,
86
+ # since this is only tangenial to the actual functionality of
87
+ # the critters themselves.
88
+ class Critter
89
+ def report_neuron_types
90
+ {
91
+ input: population.input_neurons.map {|n| n.name},
92
+ output: population.output_neurons.map{|n| n.name},
93
+ hidden: population.hidden_neurons.map{|n| n.name}
94
+ }
95
+ end
96
+
97
+ def report_genotype
98
+ genotype.genes.map{|innov, gene| {in: gene.in_neuron, out: gene.out_neuron, innov: innov}}
99
+ end
100
+
101
+ def report_phenotype
102
+ phenotype.code
103
+ end
104
+
105
+ def report
106
+ {
107
+ genotype: report_genotype,
108
+ phenotype: report_phenotype,
109
+ neuron_types: report_neuron_types
110
+ }
111
+ end
112
+ end
113
+ end