Class: Darwinning::Population

Inherits:
Object
  • Object
show all
Defined in:
lib/darwinning/population.rb

Constant Summary collapse

DEFAULT_EVOLUTION_TYPES =
[
  Darwinning::EvolutionTypes::Reproduction.new,
  Darwinning::EvolutionTypes::Mutation.new(mutation_rate: 0.10)
]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Population

Returns a new instance of Population.



13
14
15
16
17
18
19
20
21
22
23
# File 'lib/darwinning/population.rb', line 13

def initialize(options = {})
  @organism = options.fetch(:organism)
  @population_size = options.fetch(:population_size)
  @fitness_goal = options.fetch(:fitness_goal)
  @generations_limit = options.fetch(:generations_limit, 0)
  @evolution_types = options.fetch(:evolution_types, DEFAULT_EVOLUTION_TYPES)
  @members = []
  @generation = 0 # initial population is generation 0

  build_population(@population_size)
end

Instance Attribute Details

#evolution_typesObject

Returns the value of attribute evolution_types.



6
7
8
# File 'lib/darwinning/population.rb', line 6

def evolution_types
  @evolution_types
end

#fitness_goalObject

Returns the value of attribute fitness_goal.



4
5
6
# File 'lib/darwinning/population.rb', line 4

def fitness_goal
  @fitness_goal
end

#generationObject

Returns the value of attribute generation.



5
6
7
# File 'lib/darwinning/population.rb', line 5

def generation
  @generation
end

#generations_limitObject

Returns the value of attribute generations_limit.



4
5
6
# File 'lib/darwinning/population.rb', line 4

def generations_limit
  @generations_limit
end

#membersObject

Returns the value of attribute members.



4
5
6
# File 'lib/darwinning/population.rb', line 4

def members
  @members
end

#organismObject

Returns the value of attribute organism.



5
6
7
# File 'lib/darwinning/population.rb', line 5

def organism
  @organism
end

#population_sizeObject

Returns the value of attribute population_size.



5
6
7
# File 'lib/darwinning/population.rb', line 5

def population_size
  @population_size
end

Instance Method Details

#apply_non_pairwise_evolutions(members) ⇒ Object



94
95
96
97
98
99
100
101
102
# File 'lib/darwinning/population.rb', line 94

def apply_non_pairwise_evolutions(members)
  evolution_types.inject(members) do |ret, evolution_type|
    if evolution_type.pairwise?
      ret
    else
      evolution_type.evolve(ret)
    end
  end
end

#apply_pairwise_evolutions(organism, m1, m2) ⇒ Object



84
85
86
87
88
89
90
91
92
# File 'lib/darwinning/population.rb', line 84

def apply_pairwise_evolutions(organism, m1, m2)
  evolution_types.inject([m1, m2]) do |ret, evolution_type|
    if evolution_type.pairwise?
      evolution_type.evolve(organism, *ret)
    else
      ret
    end
  end
end

#best_memberObject



113
114
115
# File 'lib/darwinning/population.rb', line 113

def best_member
  @members.sort_by { |m| m.fitness }.first
end

#build_population(population_size) ⇒ Object



25
26
27
28
29
# File 'lib/darwinning/population.rb', line 25

def build_population(population_size)
  population_size.times do |i|
    @members << organism.new
  end
end

#evolution_over?Boolean

Returns:

  • (Boolean)


104
105
106
107
108
109
110
111
# File 'lib/darwinning/population.rb', line 104

def evolution_over?
  # check if the fiteness goal or generation limit has been met
  if generations_limit > 0
    generation == generations_limit || best_member.fitness == fitness_goal
  else
    generation == generations_limit || best_member.fitness == fitness_goal
  end
end

#evolve!Object



31
32
33
34
35
# File 'lib/darwinning/population.rb', line 31

def evolve!
  until evolution_over?
    make_next_generation!
  end
end

#make_next_generation!Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/darwinning/population.rb', line 65

def make_next_generation!
  temp_members = members
  used_members = []
  new_members = []

  until new_members.length == members.length / 2
    m1 = weighted_select(members - used_members)
    used_members << m1
    m2 = weighted_select(members - used_members)
    used_members << m2

    new_members << apply_pairwise_evolutions(organism, m1, m2)
  end

  new_members.flatten!
  @members = apply_non_pairwise_evolutions(new_members)
  @generation += 1
end

#set_members_fitness!(fitness_values) ⇒ Object



61
62
63
# File 'lib/darwinning/population.rb', line 61

def set_members_fitness!(fitness_values)
  members.to_enum.each_with_index { |m, i| m.fitness = fitness_values[i] }
end

#sizeObject



117
118
119
# File 'lib/darwinning/population.rb', line 117

def size
  @members.length
end

#weighted_select(members) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/darwinning/population.rb', line 37

def weighted_select(members)
  e = 0.01
  fitness_sum = members.inject(0) { |sum, m| sum + m.fitness }

  weighted_members = members.sort_by do |m|
    (m.fitness - fitness_goal).abs
  end.map do |m|
    [m, fitness_sum / ((m.fitness - fitness_goal).abs + e)]
  end

  weight_sum = weighted_members.inject(0) { |sum, m| sum + m[1] }
  pick = (0..weight_sum).to_a.sample

  weighted_members.reverse! # In order to pop from the end we need the lowest ranked first
  pick_sum = 0

  until pick_sum > pick do
    selected_member = weighted_members.pop
    pick_sum += selected_member[1]
  end

  selected_member.first
end