80 lines
2.2 KiB
Elixir
80 lines
2.2 KiB
Elixir
defmodule Genetic do
|
|
alias Types.Chromosome
|
|
|
|
def initialize(genotype, opts \\ []) do
|
|
population_size = Keyword.get(opts, :population_size, 100)
|
|
for _ <- 1..population_size, do: genotype.()
|
|
end
|
|
|
|
def evaluate(population, fitness_function, _opts \\ []) do
|
|
population
|
|
|> Enum.map(
|
|
fn chromosome ->
|
|
fitness = fitness_function.(chromosome)
|
|
age = chromosome.age + 1
|
|
%Chromosome{chromosome | fitness: fitness, age: age}
|
|
end
|
|
)
|
|
|> Enum.sort_by(& &1.fitness, &>=/2)
|
|
end
|
|
|
|
def select(population, _opts \\ []) do
|
|
population
|
|
|> Enum.chunk_every(2)
|
|
|> Enum.map(&List.to_tuple(&1))
|
|
end
|
|
|
|
def crossover(population, _opts \\ []) do
|
|
population
|
|
|> Enum.reduce([],
|
|
fn {p1, p2}, acc ->
|
|
cx_point = :rand.uniform(length(p1.genes))
|
|
{{h1,t1},{h2,t2}} =
|
|
{Enum.split(p1.genes, cx_point),
|
|
Enum.split(p2.genes, cx_point)}
|
|
{c1,c2} = {%Chromosome{p1 | genes: h1 ++ t2}, %Chromosome{p2 | genes: h2 ++ t1} }
|
|
[c1,c2 | acc]
|
|
end
|
|
)
|
|
end
|
|
|
|
def mutation(population, _opts \\ []) do
|
|
population
|
|
|> Enum.map(
|
|
fn chromosome ->
|
|
if :rand.uniform() < 0.05 do
|
|
%Chromosome{chromosome | genes: Enum.shuffle(chromosome.genes)}
|
|
else
|
|
chromosome
|
|
end
|
|
end
|
|
)
|
|
end
|
|
|
|
def run(problem, opts \\ []) do
|
|
population = initialize(&problem.genotype/0)
|
|
first_generation = 0
|
|
|
|
population
|
|
|> evolve(problem, first_generation, opts)
|
|
end
|
|
|
|
def evolve(population, problem, generation, opts \\ []) do
|
|
population = evaluate(population, &problem.fitness_function/1, opts)
|
|
best = hd(population)
|
|
IO.write("\rCurrent Best: #{best.fitness}")
|
|
if problem.terminate?(population, generation) do
|
|
best
|
|
else
|
|
population
|
|
|> select(opts)
|
|
|> crossover(opts)
|
|
|> mutation(opts)
|
|
|> evolve(problem, generation+1, opts)
|
|
end
|
|
|
|
|
|
end
|
|
|
|
end
|