class NEAT::Critter::Genotype

Genotype part of the Critter

List of connections, basically.

Also, basic phentypic expression (which may be overriden by
the expressor)

Notes

Currently, all lists of neurons and genes are Hashes. The
neurons are indexed by their own names, and the genes
are indexed by their innovation numbers.

Attributes

critter[RW]

Critter to which we belong

dangling_neurons[RW]

This will be set to true if there are dangling neurons.

dangling_neurons?[RW]

This will be set to true if there are dangling neurons.

genes[RW]

Genes keyed by innovation numbers

neural_gene_map[R]

Map neurons to the genes that marks them as output { oneu_name => [ gene_1, gene_2,… gene_n], …} Just take the in_neuron name and the weight to do the call to that neuron function with the appropriate weights

neural_inputs[R]

Instantiations of neural inputs and outputs

neural_outputs[R]

Instantiations of neural inputs and outputs

neurons[RW]

List of neurons hashed by name

Public Class Methods

new(critter, mating = false, &block) click to toggle source
Calls superclass method
# File lib/rubyneat/critter.rb, line 88
def initialize(critter, mating = false, &block)
  super critter.controller
  @critter = critter

  # Initialize basic structures
  @genes = nil
  @neural_inputs = Hash[@critter.population.input_neurons.map { |sym, ineu|
                          [sym, ineu.new(@controller, sym)]
                        }]

  @neural_outputs = Hash[@critter.population.output_neurons.map { |sym, ineu|
                          [sym, ineu.new(@controller, sym)]
                        }]
  @neurons = @neural_inputs.clone # this must be a shallow clone!
  @neurons.merge! @neural_outputs

  @controller.evolver.gen_initial_genes!(self) unless mating
  block.(self) unless block.nil?
end

Public Instance Methods

add_genes(*genes) click to toggle source

Genes added here MUST correspond to pre-existing neurons. Be sure to do #add_neurons first!!!!

# File lib/rubyneat/critter.rb, line 163
def add_genes(*genes)
  genes.each do |gene|
    raise NeatException.new "Neuron #{gene.in_neuron} missing" unless @neurons.member? gene.in_neuron
    raise NeatException.new "Neuron #{gene.out_neuron} missing" unless @neurons.member? gene.out_neuron
    @genes[gene.innovation] = gene
  end
end
add_neurons(*neus) click to toggle source

Add new neurons to the fold

# File lib/rubyneat/critter.rb, line 155
def add_neurons(*neus)
  neus.each do |neu|
    @neurons[neu.name] = neu
  end
end
dump_s() click to toggle source
# File lib/rubyneat/critter.rb, line 218
def dump_s
  to_s + "\ngenes:\n" + @genes.map{|k, gene|
    gene.to_s}.join("\n") + "\nneurons:\n" + @neurons.map{|k, neu|
    neu.to_s}.join("\n")
end
fitness_cost() click to toggle source

Calculate the cost of this genotype.

# File lib/rubyneat/critter.rb, line 213
def fitness_cost
  p = @controller.parms
  p.fitness_cost_per_neuron * @neurons.size + p.fitness_cost_per_gene * @genes.size
end
forget!() click to toggle source

Make the neurons forget their wiring.

# File lib/rubyneat/critter.rb, line 134
def forget!
  @neurons.each { |name, neu| neu.clear_graph }
  @neural_gene_map = Hash.new {|h, k| h[k] = [] }
end
innervate!(*hneus) click to toggle source

We take the neural hashes (presumably from other neurons), and innervate them. We do this in distinctions based on the neuron's names. FIXME We need to randomly select a neuron in the case of clashes. @param [Hash] hneus – hashes of neurons to innervate

# File lib/rubyneat/critter.rb, line 175
def innervate!(*hneus)
  hneus.each do |neus|
    @neurons.merge! neus.dclone
  end
end
neucleate(clean: true, &block) click to toggle source

We add genes given here to the genome. An array of genes is returned from the block and we simply add them in. @param [boolean] clean @param [Proc] block

# File lib/rubyneat/critter.rb, line 113
def neucleate(clean: true, &block)
  genes = Hash[block.(self).map { |g|
    g.genotype = self
    [g.innovation, g] }]
  if clean
    @genes = genes
  else
    @genes.merge! genes
  end
  nuke_redundancies!
end
nuke_redundancies!() click to toggle source

Remove any redundancies in the genome, any genes refering to the same two neurons. Simply choose one and delete the rest. TODO: implement nuke_redundancies!

# File lib/rubyneat/critter.rb, line 129
def nuke_redundancies!
  log.warn 'nuke_redundancies! NIY'
end
prune!() click to toggle source

Go through the list of neurons and drop any neurons not referenced by the genes.

Then go through the genes and drop any that are dangling (i.e. no matching neurons)

Then make sure that @neural_inputs and @neural_outputs reference the actual instance neurons in @neurons

TODO add this circularity check to prune!

# File lib/rubyneat/critter.rb, line 191
def prune!
  # Take care of dangling neurons
  neunames = @genes.values.map{|g| [g.in_neuron, g.out_neuron]}.flatten.to_set
  @neurons = Hash[@neurons.values.reject do |n|
    not neunames.member? n.name
  end.map do |n|
    [n.name, n]
  end]

  # Take care of dangling genes
  @genes = Hash[@genes.values.reject do |gene|
    not (@neurons.member?(gene.in_neuron) and @neurons.member?(gene.out_neuron))
  end.map do |gene|
    [gene.name, gene]
  end]

  # Make sure @neural_inputs and @neural_outputs are consistent
  @neural_inputs = Hash[@neural_inputs.values.map{|n| [n.name, @neurons[n.name]]}]
  @neural_outputs = Hash[@neural_outputs.values.map{|n| [n.name, @neurons[n.name]]}]
end
wire!() click to toggle source

Wire up the neurons based on the genes.

# File lib/rubyneat/critter.rb, line 140
def wire!
  forget!
  @genes.each do |innov, gene|
    if gene.enabled?
      raise NeatException.new "Can't find #{gene.out_neuron}" if @neurons[gene.out_neuron].nil?
      @neurons[gene.out_neuron] << @neurons[gene.in_neuron]
      @neural_gene_map[gene.out_neuron] << gene unless gene.in_neuron.nil?
    end
  end unless @genes.nil?
  if @genes.nil?
    $log.error 'Genes Not Present'
  end
end