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 to which we belong
This will be set to true if there are dangling neurons.
This will be set to true if there are dangling neurons.
Genes keyed by innovation numbers
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
Instantiations of neural inputs and outputs
Instantiations of neural inputs and outputs
List of neurons hashed by name
Public Class Methods
# 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
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 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
# 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
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
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
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
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
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
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 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