class Population

Stand for a whole population used for an evolution experiment

Constants

DEFAULT_EVOLVE_ITERATIONS
DEFAULT_FORCE_FITNESS_RECALCULATION
DEFAULT_KEEP_ALIVE_RATE
DEFAULT_MUTATION_RATE
NO_FITNESS

Attributes

fitness_target[RW]
force_fitness_recalculation[RW]
karyotypes[RW]
keep_alive_rate[R]
mutation_rate[R]

Public Class Methods

new(size, genome, fitness_calculator) click to toggle source
# File lib/population.rb, line 24
def initialize(size, genome, fitness_calculator)
  raise 'size must be strictly positive.' if size < 1
  @fitness_hash = {}
  @force_fitness_recalculation = DEFAULT_FORCE_FITNESS_RECALCULATION
  @mutation_rate = DEFAULT_MUTATION_RATE
  @keep_alive_rate = DEFAULT_KEEP_ALIVE_RATE
  @genome = Genome.new(genome)
  @fitness_calculator = fitness_calculator
  @karyotypes = Array.new(size) { @genome.create_random_karyotype }
  evaluate
end

Public Instance Methods

evolve(iterations = DEFAULT_EVOLVE_ITERATIONS) click to toggle source

This function make ou population evolving by:

  • Selecting and breeding the fittest karyotypes

  • Running the fitness evaluation on all the newly created karyotyopes

The selection process include three subprocesses:

  • Selecting the fittest individuals to keep alive

  • Mutating randomly (linear) selected individuals

  • Breeding randomly (fitness weighted) selected individuals

# File lib/population.rb, line 52
def evolve(iterations = DEFAULT_EVOLVE_ITERATIONS)
  i = 1
  while (i <= iterations) &&
        (@fitness_target.nil? || @fitness_target > @karyotypes[0].fitness)
    evolve_impl
    i += 1
  end
  self
end
keep_alive_rate=(value) click to toggle source

keep alive rate setter function Accept values in the range [0,1]

# File lib/population.rb, line 20
def keep_alive_rate=(value)
  @keep_alive_rate = validate!(:keep_alive_rate, value)
end
mutation_rate=(value) click to toggle source

mutation rate setter function Accept values in the range [0,1]

# File lib/population.rb, line 14
def mutation_rate=(value)
  @mutation_rate = validate!(:mutation_rate, value)
end
size() click to toggle source
# File lib/population.rb, line 41
def size
  @karyotypes.size
end

Private Instance Methods

build_new_karyotypes(keep_alive_count, mutation_count) click to toggle source
# File lib/population.rb, line 99
def build_new_karyotypes(keep_alive_count, mutation_count)
  remaining = @karyotypes.size - mutation_count - keep_alive_count
  @karyotypes[0, keep_alive_count]
    .concat(Array.new(mutation_count) { create_random_mutation })
    .concat(Array.new(remaining) { random_breed })
end
cached_fitness(karyotype) click to toggle source
# File lib/population.rb, line 87
def cached_fitness(karyotype)
  @fitness_hash.fetch(karyotype.to_md5) { fitness karyotype }
end
create_random_mutation() click to toggle source
# File lib/population.rb, line 110
def create_random_mutation
  linear_random_select.copy.mutate
end
evaluate() click to toggle source

Run the fitness function for all karyotypes, and sort it by fitness

# File lib/population.rb, line 67
def evaluate
  if @force_fitness_recalculation
    @karyotypes.each { |karyotype| update!(karyotype, fitness(karyotype)) }
  else
    @karyotypes.each do |karyotype|
      update!(karyotype, cached_fitness(karyotype)) if karyotype.fitness.nil?
    end
  end
  @karyotypes.sort_by! { |karyotype| - karyotype.fitness }
end
evolve_impl() click to toggle source
# File lib/population.rb, line 91
def evolve_impl
  # Keeping alive a specific amount of the best karyotypes
  keep_alive_count = Integer(@karyotypes.size * @keep_alive_rate)
  mutation_count = Integer(@karyotypes.size * @mutation_rate)
  @karyotypes = build_new_karyotypes(keep_alive_count, mutation_count)
  evaluate
end
fitness(karyotype) click to toggle source
# File lib/population.rb, line 83
def fitness(karyotype)
  @fitness_calculator.call(karyotype)
end
fitness_weighted_random_select() click to toggle source
# File lib/population.rb, line 114
def fitness_weighted_random_select
  @karyotypes[
    @karyotypes.size -
    Integer(Math.sqrt(Math.sqrt(1 + rand(@karyotypes.size**4 - 1))))
  ]
end
linear_random_select() click to toggle source
# File lib/population.rb, line 106
def linear_random_select
  @karyotypes[rand @karyotypes.size]
end
random_breed() click to toggle source
# File lib/population.rb, line 121
def random_breed
  fitness_weighted_random_select + fitness_weighted_random_select
end
update!(karyotype, fitness) click to toggle source
# File lib/population.rb, line 78
def update!(karyotype, fitness)
  karyotype.fitness = fitness
  @fitness_hash[karyotype.to_md5] = karyotype.fitness
end
validate!(label, value) click to toggle source
# File lib/population.rb, line 125
def validate!(label, value)
  raise "#{label} value must be included in [0,1]" unless value.between?(0, 1)
  value
end