Genetic Algorithms in Elixir: Chapter 9/10 ebook-1.02 Genealogy not working well

Chapter 9/10 ebook-1.02 Genealogy not working well.

As already mentioned in one of the errata the ID of every chromosome is the same.
So plotting the tree with libgraph gives many problems.
For instance parents are not unique, they are all inserted.

I did change a few things. Nodes are now unique.
Labels are much smaller, because I changed them to fitness.
You now get a nice plot with small nodes.

This is the relevant part of genealogy.ex:

def handle_cast({:add_chromosome, parent, child}, genealogy) do
  new_genealogy =
    genealogy
    |> Graph.add_vertex(child.id, child.fitness)
    |> Graph.add_edge(parent.id, child.id)

  {:noreply, new_genealogy}
end

def handle_cast({:add_chromosome, parent_a, parent_b, child}, genealogy) do
  new_genealogy =
    genealogy
    |> Graph.add_vertex(child.id, child.fitness)
    |> Graph.add_edge(parent_a.id, child.id)
    |> Graph.add_edge(parent_b.id, child.id)

  {:noreply, new_genealogy}
end

The last part of crossover().
ID’s are now unique. Fitness are assigned

c1 = %Chromosome{
  c1
  | id: Base.encode16(:crypto.strong_rand_bytes(64)),
    fitness: Enum.sum(c1.genes)
    }

c2 = %Chromosome{
  c2
  | id: Base.encode16(:crypto.strong_rand_bytes(64)),
    fitness: Enum.sum(c2.genes)
}

        Utilities.Genealogy.add_chromosome(p1, p2, c1)
        Utilities.Genealogy.add_chromosome(p1, p2, c2)
        [c1 | [c2 | acc]]
    

I did use the following tiger_simulation:

defmodule TigerSimulation do
  @behaviour Problem
  alias Types.Chromosome

  @impl true
  def genotype do
    genes = for _ <- 1..8, do: Enum.random(0..1)
    %Chromosome{genes: genes, size: 8, fitness: Enum.sum(genes), id: Base.encode16(:crypto.strong_rand_bytes(64))}
  end

  @impl true
  def fitness_function(chromosome) do
    tropic_scores = [0.0, 3.0, 2.0, 1.0, 0.5, 1.0, -1.0, 0.0]
    #    tundra_scores = [1.0, 3.0, -2.0, -1.0, 0.5, 2.0, 1.0, 0.0]
    traits = chromosome.genes

    traits
    |> Enum.zip(tropic_scores)
    |> Enum.map(fn {t, s} -> t * s end)
    |> Enum.sum()
  end

  @impl true
  def terminate?(_population, generation), do: generation == 10
end

_tiger =
  Genetic.run(TigerSimulation,
    population_size: 8,
    selection_rate: 0.8,
    mutation_rate: 0.1
  )

genealogy = Utilities.Genealogy.get_tree()

{:ok, dot} = Graph.Serializers.DOT.serialize(genealogy)
{:ok, dotfile} = File.open("tiger_simulation.dot", [:write])
:ok = IO.binwrite(dotfile, dot)
:ok = File.close(dotfile)

Tip: In Intellij Idea you have a nice plugin for .dot files: Dot plus!
If selection <1 you wil see also loose nodes. Nice!