summaryrefslogtreecommitdiff
path: root/ga.lua
blob: 8b5b0e26bd90b4a27a954693ff2883749318a9fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
local module = {}
setmetatable(module, {__index=_G})
setfenv(1, module)


local GA = {}
local metaGA = { __index=GA }


function module.createGA(population, operators)
  local self = { 
    population=population,
    operators=operators,
  }

  local totalWeight = 
    (operators.crossWeight or 1) + 
    (operators.mutationWeight or 1) + 
    (operators.reproductionWeight or 1)

  self.operators.crossDensity = (operators.crossWeight or 1) / totalWeight
  self.operators.mutationDensity = self.operators.crossDensity + ((operators.mutationWeight or 1) / totalWeight)
  self.operators.reproductionDensity = 1

  setmetatable(self, metaGA)
  return self
end


function GA.evaluate(self)
  self.fitnesses = {}
  for i, p in ipairs(self.population) do
    self.fitnesses[i] = self.operators.evaluate(p)
    if i == 1 or self.fitnesses[i] > self.maxFitness then
      self.maxFitness = self.fitnesses[i]
      self.bestMember = i
    end
  end
end


function GA.tournamentPick(self, k)
  local tournamentPop = {}
  while #tournamentPop < k do
    table.insert(tournamentPop, math.ceil(#self.population * math.random()))
  end
  table.sort(tournamentPop, function(a, b) return self.fitnesses[a] > self.fitnesses[b] end)
  return tournamentPop[1]
end


function GA.stochasticPick(self)
  local idx = math.ceil(#self.population * math.random())
  local f = self.fitnesses[idx] / self.maxFitness
  if math.random() < f then
    return idx
  else
    return self:stochasticPick()
  end
end


function GA.createNewPopulation(self, k)
  k = k or 128
  local newPop = {}
  while #newPop < #self.population do
    local r = math.random()
    if r < self.operators.crossDensity then
      local a = self:tournamentPick(k)
      local b = self:tournamentPick(k)
      table.insert(newPop, self.operators.crossover(self.population[a], self.population[b]))
    elseif r < self.operators.mutationDensity then
      local a = self:tournamentPick(k)
      table.insert(newPop, self.operators.mutate(self.population[a]))
    elseif r < self.operators.reproductionDensity then
      local a = self:tournamentPick(k)
      table.insert(newPop, self.population[a])
    end
  end
  return newPop
end


function GA.step(self)
  self.fitnesses = nil
  self.maxFitness = nil
  self:evaluate()
  self.population = self:createNewPopulation()
  return self.maxFitness
end


return module