class GA::Zoo

Attributes

after_select_callback[R]
before_fitness_callback[R]
unit_cls[R]

Public Class Methods

new(unit_cls) click to toggle source
# File lib/ga/zoo.rb, line 5
def initialize(unit_cls)
  @unit_cls = unit_cls
  @debug = false
  @before_fitness_callback = nil
  @after_select_callback = nil
  @elite_policy = false
  @select_sample = 2
end

Public Instance Methods

after_select(&block) click to toggle source
# File lib/ga/zoo.rb, line 48
def after_select(&block)
  @after_select_callback = block
end
before_init_fitness(&block) click to toggle source
# File lib/ga/zoo.rb, line 44
def before_init_fitness(&block)
  @before_fitness_callback = block
end
debug!() click to toggle source
# File lib/ga/zoo.rb, line 32
def debug!
  @debug = true
end
elite_policy!() click to toggle source
# File lib/ga/zoo.rb, line 40
def elite_policy!
  @elite_policy = true
end
evolve(units = 32, generations = 100, crossover_rate = 0.8, mutation_rate = 0.15) click to toggle source
# File lib/ga/zoo.rb, line 14
def evolve(units = 32, generations = 100, crossover_rate = 0.8, mutation_rate = 0.15)
  units = units.times.map { unit_cls.random_new } if units.is_a?(Fixnum)
  @start_at = Time.now

  generations.times do |i|
    @before_fitness_callback.call(units, i + 1) if @before_fitness_callback
    output_debug_info(units, generations, i + 1) if @debug

    units = select_units(units)
    @after_select_callback.call(units, i + 1) if @after_select_callback

    cross(units, crossover_rate)
    mutate(units, mutation_rate)
  end

  return units
end
set_select_sample(val) click to toggle source
# File lib/ga/zoo.rb, line 36
def set_select_sample(val)
  @select_sample = val
end

Private Instance Methods

cross(units, rate) click to toggle source
# File lib/ga/zoo.rb, line 73
def cross(units, rate)
  last_index = nil

  units.each_with_index do |unit, index|
    next if @elite_policy && index == 0
    next if rand() >= rate

    if last_index
      units[last_index].cross!(unit)
      # recalculate fitness
      units[last_index].fitness = unit.fitness = nil
      last_index = nil
    else
      last_index = index
    end
  end
end
mutate(units, rate) click to toggle source
# File lib/ga/zoo.rb, line 91
def mutate(units, rate)
  units.each_with_index do |unit, index|
    next if @elite_policy && index == 0
    next if rand() >= rate
    unit.mutate!
    # recalculate fitness
    unit.fitness = nil
  end
end
output_debug_info(units, generations, generation) click to toggle source
# File lib/ga/zoo.rb, line 101
def output_debug_info(units, generations, generation)
  units.sort!
  info = [
    "[#{(Time.now - @start_at).to_f}]",
    "GA-#{generation}/#{generations} #{units.count}-#{units[-1].genome.length}",
    ' fitness: '
  ]

  if units.length <= 7 then
    info << units.map(&:fitness).join(', ')
  else
    info << "#{units[0..2].map(&:fitness).join(', ')}"
    info << " ... #{units[units.length / 2].fitness}"
    info << " ... #{units[-5..-1].map(&:fitness).join(', ')}"
  end

  puts info.join
end
select_units(units) click to toggle source
# File lib/ga/zoo.rb, line 55
def select_units(units)
  new_units = units.map do
    ou = units.sample(@select_sample).max
    unit_cls.new(ou.genome).tap {|u| u.fitness = ou.fitness }
  end

  if @elite_policy
    min_index = new_units.index(new_units.min)
    if min_index != 0 then
      new_units[min_index], new_units[0] = new_units[0], new_units[min_index]
    end
    max_unit = units.max
    new_units[0] = unit_cls.new(max_unit.genome)
  end

  new_units
end