class NEAT::Population

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.

Population Reporting

The tangenial reporting needs for the Population module
are extracted here simply because they do not directly relate
to the operations of the Population.

Attributes

critters[RW]

list of critter in this population

fitness[R]

Overall population fitness and novelty

hidden_neurons[RW]

List of possible neuron classes for hidden neurons.

input_neurons[RW]

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

novelty[R]

Overall population fitness and novelty

output_neurons[RW]

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

species[R]

Hash list of species lists

traits[RW]

Public Class Methods

new(c, &block) click to toggle source

Create initial (ramdom) population of critters

Calls superclass method
# 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

Public Instance Methods

analyze!() click to toggle source

Alalyze evaluation results.

# File lib/rubyneat/population.rb, line 80
def analyze!
   @critters.each { |critter| @controller.evaluator.analyze_for_fitness! critter }
end
best_critter() click to toggle source

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

# 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_hash() click to toggle source

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.

# File lib/rubyneat/reporting.rb, line 72
def critter_hash
  @critter_hash ||= critters.inject({}){|memo, crit| memo[crit.name]=crit; memo}
end
dump_s() click to toggle source
# File lib/rubyneat/population.rb, line 190
def dump_s
  to_s + "\npopulation:\n" + @critters.map{|crit| crit.dump_s }.join("\n")
end
evaluate!() click to toggle source

Called for each sequence.

# File lib/rubyneat/population.rb, line 75
def evaluate!
   @critters.each { |critter| critter.evaluate! }
end
evolve() click to toggle source

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

# File lib/rubyneat/population.rb, line 86
def evolve
  @controller.evolver.evolve self
end
express!() click to toggle source

Express the entire population.

# File lib/rubyneat/population.rb, line 70
def express!
   @critters.each { |critter| critter.express! }
end
find_critters(*names) click to toggle source

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.

# File lib/rubyneat/reporting.rb, line 79
def find_critters(*names)
  names.map{|name| critter_hash[name.to_sym]}
end
initialize_for_recurrence!() click to toggle source

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

# File lib/rubyneat/population.rb, line 60
def initialize_for_recurrence!
  @critters.each {|crit| crit.initialize_neurons!}
end
mutate!() click to toggle source

Mutate the genes and neurons.

# File lib/rubyneat/population.rb, line 65
def mutate!
  @controller.evolver.mutate! self
end
report() click to toggle source

Generate a report on the state of this population.

# 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_fit() click to toggle source

Find the best fit critter

# File lib/rubyneat/reporting.rb, line 39
def report_best_fit
  best_critter.phenotype.code
end
report_critters() click to toggle source

Create a hash of critter names and fitness values

# File lib/rubyneat/reporting.rb, line 49
def report_critters
  critters.inject({}){|memo, critter| memo[critter.name] = critter.fitness; memo }
end
report_fitness() click to toggle source

report on many fitness metrics

# 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_species() click to toggle source

report on the best and worst species

# File lib/rubyneat/reporting.rb, line 31
def report_fitness_species
  {
      best: nil,
      worst: nil,
  }
end
report_worst_fit() click to toggle source

Find the worst fit critter

# File lib/rubyneat/reporting.rb, line 44
def report_worst_fit
  worst_critter.phenotype.code
end
speciate!() click to toggle source
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

# 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_critter() click to toggle source

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

# 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