lib/ga/zoo.rb in ga-0.1.1 vs lib/ga/zoo.rb in ga-0.1.2

- old
+ new

@@ -1,23 +1,29 @@ module GA class Zoo - attr_reader :unit_cls + attr_reader :unit_cls, :before_fitness_callback, :after_select_callback 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 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 @@ -25,37 +31,52 @@ def debug! @debug = true end + def set_select_sample(val) + @select_sample = val + end + + def elite_policy! + @elite_policy = true + end + def before_init_fitness(&block) @before_fitness_callback = block end + def after_select(&block) + @after_select_callback = block + end + private def select_units(units) new_units = units.map do - ou = units.sample(3).max + ou = units.sample(@select_sample).max unit_cls.new(ou.genome).tap {|u| u.fitness = ou.fitness } end - # 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] + 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[0] = unit_cls.new(units.max.genome) + new_units end def cross(units, rate) last_index = nil units.each_with_index do |unit, index| - next if index == 0 + next if @elite_policy && index == 0 next if rand() >= rate if last_index units[last_index].cross!(unit) # recalculate fitness @@ -67,28 +88,30 @@ end end def mutate(units, rate) units.each_with_index do |unit, index| - next if index == 0 + next if @elite_policy && index == 0 next if rand() >= rate unit.mutate! # recalculate fitness unit.fitness = nil end end 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