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

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.
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