Class: NEAT::Critter::Genotype

Inherits:
NeatOb
  • Object
show all
Defined in:
lib/rubyneat/critter.rb

Overview

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.

Defined Under Namespace

Classes: Gene

Instance Attribute Summary collapse

Attributes inherited from NeatOb

#controller, #name

Instance Method Summary collapse

Methods inherited from NeatOb

attr_neat, log, #log, #to_s

Constructor Details

#initialize(critter, mating = false, &block) ⇒ Genotype

Returns a new instance of Genotype.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# 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

Instance Attribute Details

#critterObject

Critter to which we belong



67
68
69
# File 'lib/rubyneat/critter.rb', line 67

def critter
  @critter
end

#dangling_neuronsObject Also known as: dangling_neurons?

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



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

def dangling_neurons
  @dangling_neurons
end

#genesObject

Genes keyed by innovation numbers



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

def genes
  @genes
end

#neural_gene_mapObject (readonly)

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



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

def neural_gene_map
  @neural_gene_map
end

#neural_inputsObject (readonly)

Instantiations of neural inputs and outputs



76
77
78
# File 'lib/rubyneat/critter.rb', line 76

def neural_inputs
  @neural_inputs
end

#neural_outputsObject (readonly)

Instantiations of neural inputs and outputs



76
77
78
# File 'lib/rubyneat/critter.rb', line 76

def neural_outputs
  @neural_outputs
end

#neuronsObject

List of neurons hashed by name



73
74
75
# File 'lib/rubyneat/critter.rb', line 73

def neurons
  @neurons
end

Instance Method Details

#add_genes(*genes) ⇒ Object

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



163
164
165
166
167
168
169
# 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) ⇒ Object

Add new neurons to the fold



155
156
157
158
159
# File 'lib/rubyneat/critter.rb', line 155

def add_neurons(*neus)
  neus.each do |neu|
    @neurons[neu.name] = neu
  end
end

#dump_sObject



218
219
220
221
222
# 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_costObject

Calculate the cost of this genotype.



213
214
215
216
# 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!Object

Make the neurons forget their wiring.



134
135
136
137
# 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) ⇒ Object

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.

Parameters:

  • hneus (Hash)

    – hashes of neurons to innervate



175
176
177
178
179
# File 'lib/rubyneat/critter.rb', line 175

def innervate!(*hneus)
  hneus.each do |neus|
    @neurons.merge! neus.dclone
  end
end

#neucleate(clean: true, &block) ⇒ Object

We add genes given here to the genome. An array of genes is returned from the block and we simply add them in.

Parameters:

  • clean (boolean) (defaults to: true)
  • block (Proc)


113
114
115
116
117
118
119
120
121
122
123
# 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!Object

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!



129
130
131
# File 'lib/rubyneat/critter.rb', line 129

def nuke_redundancies!
  log.warn 'nuke_redundancies! NIY'
end

#prune!Object

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!



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# 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!Object

Wire up the neurons based on the genes.



140
141
142
143
144
145
146
147
148
149
150
151
152
# 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