Class: NEAT::Population

Inherits:
NeatOb
  • Object
show all
Defined in:
lib/rubyneat/reporting.rb,
lib/rubyneat/population.rb

Overview

Population of NEAT Critters.

The Population In ourselves we have the pool of neurons the critters all use. the pool of neurons are indirects, of course, as during phenotype expression, all the phenotypes shall be created individually.

Instance Attribute Summary collapse

Attributes inherited from NeatOb

#controller, #name

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from NeatOb

attr_neat, log, #log, #to_s

Constructor Details

#initialize(c, &block) ⇒ Population

Create initial (ramdom) population of critters



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/rubyneat/population.rb', line 42

def initialize(c, &block)
  super
  @input_neurons = c.neural_inputs.clone
  @output_neurons = c.neural_outputs.clone
  @hidden_neurons = unless c.neural_hidden.nil?
                      c.neural_hidden
                    else
                      c.neuron_catalog.keep_if {|n| not n.input?}
                    end
  @critters = (0 ... c.parms.start_population_size || c.parms.population_size).map do
    Critter.new(self)
  end

  block.(self) unless block.nil?
end

Instance Attribute Details

#crittersObject

list of critter in this population



30
31
32
# File 'lib/rubyneat/population.rb', line 30

def critters
  @critters
end

#fitnessObject (readonly)

Overall population fitness and novelty



33
34
35
# File 'lib/rubyneat/population.rb', line 33

def fitness
  @fitness
end

#hidden_neuronsObject

List of possible neuron classes for hidden neurons.



21
22
23
# File 'lib/rubyneat/population.rb', line 21

def hidden_neurons
  @hidden_neurons
end

#input_neuronsObject

Ordered list or hash of input neuron classes (all critters generated here shall have this)



18
19
20
# File 'lib/rubyneat/population.rb', line 18

def input_neurons
  @input_neurons
end

#noveltyObject (readonly)

Overall population fitness and novelty



33
34
35
# File 'lib/rubyneat/population.rb', line 33

def novelty
  @novelty
end

#output_neuronsObject

Ordered list or hash of output neuron classes (all critters generated here shall have this)



25
26
27
# File 'lib/rubyneat/population.rb', line 25

def output_neurons
  @output_neurons
end

#speciesObject (readonly)

Hash list of species lists



36
37
38
# File 'lib/rubyneat/population.rb', line 36

def species
  @species
end

#traitsObject

Returns the value of attribute traits.



27
28
29
# File 'lib/rubyneat/population.rb', line 27

def traits
  @traits
end

Class Method Details

.compactify!(parm) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/rubyneat/population.rb', line 108

def @species.compactify!(parm)
  mutt = self[:mutt] = self.map { |k, splist| [k, splist]}.reject {|k, splist|
    splist.size >= parm.smallest_species
  }.map { |k, splist|
    self.delete k
    splist
  }.flatten

  # FIXME this code is not dry!!!!
  def mutt.fitness=(fit)
    @fitness = fit
  end

  def mutt.fitness
    @fitness
  end

  self.delete :mutt if self[:mutt].empty?
end

.evaluate!Object



102
103
104
105
106
# File 'lib/rubyneat/population.rb', line 102

def @species.evaluate!
  self.each do |k, sp|
    sp.fitness = sp.map{|crit| crit.fitness}.reduce{|a,b| a+b} / sp.size
  end
end

.member?(crit) ⇒ Boolean

lists keyed by representative critter

Returns:

  • (Boolean)


98
99
100
# File 'lib/rubyneat/population.rb', line 98

def @species.member?(crit)
  super.member?(crit) or self.map{|k, li| li.member? crit}.reduce{|t1, t2| t1 or t2 }
end

Instance Method Details

#analyze!Object

Alalyze evaluation results.



80
81
82
# File 'lib/rubyneat/population.rb', line 80

def analyze!
   @critters.each { |critter| @controller.evaluator.analyze_for_fitness! critter }
end

#best_critterObject

The “best critter” is the critter with the lowest (closet to zero) fitness rating. TODO: DRY up best_critter and worst_critter



172
173
174
175
176
177
178
# File 'lib/rubyneat/population.rb', line 172

def best_critter
  unless @controller.compare_func.empty?
    @critters.min {|a, b| @controller.compare_func_hook(a.fitness, b.fitness) }
  else
    @critters.min {|a, b| a.fitness <=> b.fitness}
  end
end

#critter_hashObject

TODO: we should probably provide a means to invalidate this cache. TODO: but in most reasonable use cases this would not be called until TODO: after all the critters have been created.



72
73
74
# File 'lib/rubyneat/reporting.rb', line 72

def critter_hash
  @critter_hash ||= critters.inject({}){|memo, crit| memo[crit.name]=crit; memo}
end

#dump_sObject



190
191
192
# File 'lib/rubyneat/population.rb', line 190

def dump_s
  to_s + "\npopulation:\n" + @critters.map{|crit| crit.dump_s }.join("\n")
end

#evaluate!Object

Called for each sequence.



75
76
77
# File 'lib/rubyneat/population.rb', line 75

def evaluate!
   @critters.each { |critter| critter.evaluate! }
end

#evolveObject

Call this after evaluation. Returns a newly-evolved population.



86
87
88
# File 'lib/rubyneat/population.rb', line 86

def evolve
  @controller.evolver.evolve self
end

#express!Object

Express the entire population.



70
71
72
# File 'lib/rubyneat/population.rb', line 70

def express!
   @critters.each { |critter| critter.express! }
end

#find_critters(*names) ⇒ Object

Retrive list of critters given from parameters given, names of critters. Return the results in an array. Names given must exist. Can be either strings or symbols or something that can be reduced to symbols, at least.



79
80
81
# File 'lib/rubyneat/reporting.rb', line 79

def find_critters(*names)
  names.map{|name| critter_hash[name.to_sym]}
end

#initialize_for_recurrence!Object

Make sure all critters are reset and prepared for recurrent network evaluation.



60
61
62
# File 'lib/rubyneat/population.rb', line 60

def initialize_for_recurrence!
  @critters.each {|crit| crit.initialize_neurons!}
end

#mutate!Object

Mutate the genes and neurons.



65
66
67
# File 'lib/rubyneat/population.rb', line 65

def mutate!
  @controller.evolver.mutate! self
end

#reportObject

Generate a report on the state of this population.



55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/rubyneat/reporting.rb', line 55

def report
  [
      self,
      {
          generation:      generation,
          fitness:         report_fitness,
          fitness_species: report_fitness_species,
          best_critter:    report_best_fit,
          worst_critter:   report_worst_fit,
          all_critters:    report_critters,
      }
  ]
end

#report_best_fitObject

Find the best fit critter



39
40
41
# File 'lib/rubyneat/reporting.rb', line 39

def report_best_fit
  best_critter.phenotype.code
end

#report_crittersObject

Create a hash of critter names and fitness values



49
50
51
# File 'lib/rubyneat/reporting.rb', line 49

def report_critters
  critters.inject({}){|memo, critter| memo[critter.name] = critter.fitness; memo }
end

#report_fitnessObject

report on many fitness metrics



22
23
24
25
26
27
28
# File 'lib/rubyneat/reporting.rb', line 22

def report_fitness
  {
      overall: critters.map{|critter| critter.fitness}.reduce{|m, f| m + f} / critters.size,
      best: best_critter.fitness,
      worst: worst_critter.fitness,
  }
end

#report_fitness_speciesObject

report on the best and worst species



31
32
33
34
35
36
# File 'lib/rubyneat/reporting.rb', line 31

def report_fitness_species
  {
      best: nil,
      worst: nil,
  }
end

#report_worst_fitObject

Find the worst fit critter



44
45
46
# File 'lib/rubyneat/reporting.rb', line 44

def report_worst_fit
  worst_critter.phenotype.code
end

#speciate!Object

Group critters into species Note that the @species objects have useful singleton methods:

  • @species.member? – checks all of the lists for membership, not just the hash

  • @species.fitness – fitness of the entire species



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/rubyneat/population.rb', line 95

def speciate!
  # We blow away existing species and create our own member? function
  @species = {} # lists keyed by representative critter
  def @species.member?(crit)
    super.member?(crit) or self.map{|k, li| li.member? crit}.reduce{|t1, t2| t1 or t2 }
  end

  def @species.evaluate!
    self.each do |k, sp|
      sp.fitness = sp.map{|crit| crit.fitness}.reduce{|a,b| a+b} / sp.size
    end
  end

  def @species.compactify!(parm)
    mutt = self[:mutt] = self.map { |k, splist| [k, splist]}.reject {|k, splist|
      splist.size >= parm.smallest_species
    }.map { |k, splist|
      self.delete k
      splist
    }.flatten

    # FIXME this code is not dry!!!!
    def mutt.fitness=(fit)
      @fitness = fit
    end

    def mutt.fitness
      @fitness
    end

    self.delete :mutt if self[:mutt].empty?
  end

  # Some convience parms
  parm = @controller.parms

  # And so now we iterate...
  @critters.each do |crit|
    wearein = false
    @species.each do |ck, list|
      delta = crit.compare(ck)
      #log.debug { "delta for #{crit} and #{ck} is #{delta}" }
      if delta < parm.compatibility_threshold
        list << crit
        wearein = true
        break
      end
    end

    # New species?
    unless wearein
      @species[crit] = species = [crit]
      def species.fitness=(fit)
        @fitness = fit
      end
      def species.fitness
        @fitness
      end
    end
  end

  # Compactify the species if less than smallest_species
  @species.compactify! parm

  # And now we evaluate all species for fitness...
  @species.evaluate!

  # Dump for debugging reasons
  @species.each do |k, sp|
    log.debug ">> Species #{k} has #{sp.size} members with a #{sp.fitness} fitness"
  end

end

#worst_critterObject

The “worst critter” is the critter with the highest (away from zero) fitness rating.



182
183
184
185
186
187
188
# File 'lib/rubyneat/population.rb', line 182

def worst_critter
  unless @controller.compare_func.empty?
    @critters.max {|a, b| @controller.compare_func_hook(a.fitness, b.fitness) }
  else
    @critters.max {|a, b| a.fitness <=> b.fitness}
  end
end