avatarThe Pragmatic Programmers

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

3484

Abstract

span>​) ​ |> <span class="hljs-title class_">Enum</span>.sum() ​ ​<span class="hljs-keyword">end</span>​ ​ ​ <span class="hljs-variable">@impl</span> <span class="hljs-literal">true</span> ​ ​<span class="hljs-function"><span class="hljs-keyword">def</span><span class="hljs-title">terminate?</span></span>(_population, generation), ​<span class="hljs-keyword">do</span>​: generation == <span class="hljs-number">1000</span> ​ ​ ​<span class="hljs-function"><span class="hljs-keyword">def</span><span class="hljs-title">average_tiger</span></span>(population) ​<span class="hljs-keyword">do</span>​ ​ genes = <span class="hljs-title class_">Enum</span>.map(population, & &<span class="hljs-number">1</span>.genes) ​ fitnesses = <span class="hljs-title class_">Enum</span>.map(population, & &<span class="hljs-number">1</span>.fitness) ​ ages = <span class="hljs-title class_">Enum</span>.map(population, & &<span class="hljs-number">1</span>.age) ​ num_tigers = length(population) ​ ​ avg_fitness = <span class="hljs-title class_">Enum</span>.sum(fitnesses) / num_tigers ​ avg_age = <span class="hljs-title class_">Enum</span>.sum(ages) / num_tigers ​ avg_genes = ​ genes ​ |> <span class="hljs-title class_">Enum</span>.zip() ​ |> <span class="hljs-title class_">Enum</span>.map(& <span class="hljs-title class_">Enum</span>.sum(<span class="hljs-title class_">Tuple</span>.to_list(&<span class="hljs-number">1</span>)) / num_tigers) ​ ​ %<span class="hljs-title class_">Chromosome</span>{​<span class="hljs-symbol">genes:</span>​ avg_genes, ​<span class="hljs-symbol">age:</span>​ avg_age, ​<span class="hljs-symbol">fitness:</span>​ avg_fitness} ​ ​<span class="hljs-keyword">end</span>​ ​ ​<span class="hljs-keyword">end</span>​ ​ ​ tiger = <span class="hljs-title class_">Genetic</span>.run(<span class="hljs-title class_">TigerSimulation</span>, ​ ​<span class="hljs-symbol">population_size:</span><span class="hljs-number">20</span>, ​ ​<span class="hljs-symbol">selection_rate:</span><span class="hljs-number">0.9</span>, ​ ​<span class="hljs-symbol">mutation_rate:</span><span class="hljs-number">0.1</span>, ​ ​<span class="hljs-symbol">statistics:</span>​ ​ %{​<span class="hljs-symbol">average_tiger:</span>​ &<span class="hljs-title class_">TigerSimulation</span>.average_tiger/<span class="hljs-number">1</span>}) ​ ​ <span class="hljs-title class_">IO</span>.write(​<span class="hljs-string">"​​\n"</span>​) ​ <span class="hljs-title class_">IO</span>.inspect(tiger) ​ genealogy = <span class="hljs-title class_">Utilities</span>.<span class="hljs-title class_">Genealogy</span>.get_tree() ​ ​ <span class="hljs-title class_">IO</span>.inspect(<span class="hljs-title class_">Graph</span>.vertices(genealogy))</pre></div><p id="c406">You’ll need to start by adjusting the termination criteria. Currently, the algorithm is set to terminate after 1000 generations. For the purposes of this chapter, you’ll decrease this number significantly to make it easier to visualize the genealogy. In this example, you’ll decrease the number of generations to 1 and the population size to 5:</p><div id="ad9e"><pre>​ ​defmodule​ TigerSimulation ​<span class="hljs-keyword">do</span>​ ​ ​<span class="hljs-comment"># ...​</span> ​ ​def​ terminate?(_po

Options

pulation, generation), ​<span class="hljs-keyword">do</span>​: generation == <span class="hljs-number">0</span> ​ ​<span class="hljs-comment"># ...​</span> ​ ​end​ ​ ​ tiger = Genetic.<span class="hljs-title function_ invoke__">run</span>(TigerSimulation, ​ ​<span class="hljs-attr">population_size</span>:​ <span class="hljs-number">2</span>, ​ ​<span class="hljs-attr">selection_rate</span>:​ <span class="hljs-number">1.0</span>, ​ ​<span class="hljs-attr">mutation_rate</span>:​ <span class="hljs-number">0.0</span>, ​ ​<span class="hljs-attr">statistics</span>:​ ​ %{​<span class="hljs-attr">average_tiger</span>:​ &TigerSimulation.average_tiger/<span class="hljs-number">1</span>})</pre></div><p id="1a9e">Next, you need to export your genealogy tree to a DOT file, like this:</p><div id="5ad4"><pre>​ genealogy = Utilities<span class="hljs-selector-class">.Genealogy</span><span class="hljs-selector-class">.get_tree</span>() ​ ​ {​:ok​, dot} = Graph<span class="hljs-selector-class">.Serializers</span><span class="hljs-selector-class">.DOT</span><span class="hljs-selector-class">.serialize</span>(genealogy) ​ {​:ok​, dotfile} = File<span class="hljs-selector-class">.open</span>(​<span class="hljs-string">"​​tiger_simulation.dot"</span>​, <span class="hljs-selector-attr">[​:write​]</span>) ​ ​:ok​ = IO<span class="hljs-selector-class">.binwrite</span>(dotfile, dot) ​ ​:ok​ = File<span class="hljs-selector-class">.close</span>(dotfile)</pre></div><p id="4ebc">In this snippet, you serialize the genealogy tree to a DOT binary using the libgraph API. DOT is a standard serialized graph format. You then create a new file and write the serialized graph to it.</p><p id="c700">When you run your algorithm, you’ll see tiger_simulation.dot in your main directory. Now, to visualize, you can download Graphviz<a href="https://readmedium.com/what-you-learned-558f65085768">[6]</a> or navigate to Webgraphviz<a href="https://readmedium.com/what-you-learned-558f65085768">[7]</a> and copy the contents of tiger_simulation.dot into the text field. Your genealogy tree will end up looking something like the image.</p><figure id="bf4d"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*TmL3Mxb5xgcq5GUGocDA6A.png"><figcaption></figcaption></figure><p id="09b5">Notice the genealogy is essentially a tree starting from 2 chromosomes in the first generation and spanning out from there. This type of visualization is excellent in showing how the fittest chromosome transformed over time.</p><p id="500b"><i>👈 <a href="https://readmedium.com/chapter-10-visualizing-the-results-d54a4b729a37">Chapter 10 Visualizing the Results</a> | <a href="https://readmedium.com/table-of-contents-879fc8614df">TOC</a> | <a href="https://readmedium.com/visualizing-basic-statistics-84aef732dd5">Visualizing Basic Statistics</a> 👉</i></p><p id="c1e8"><i>Genetic Algorithms in Elixir by Sean Moriarity can be purchased in other book formats <a href="https://pragprog.com/titles/smgaelixir">directly from the Pragmatic Programmers</a>. If you notice a code error or formatting mistake, please let us know <a href="https://readmedium.com/how-to-report-errata-4e164674347a">here</a> so that we can fix it.</i></p><figure id="66b9"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*U2E2a23SuM4520w9CUXSWw.jpeg"><figcaption></figcaption></figure></article></body>

Visualizing the Genealogy of the Tiger Evolution

Genetic Algorithms in Elixir — by Sean Moriarity (76 / 101)

👈 Chapter 10 Visualizing the Results | TOC | Visualizing Basic Statistics 👉

In the previous chapter, you fully integrated some basic tracking mechanisms into your genetic algorithm framework. These mechanisms allowed you to track statistics on age, fitness, and pretty much any other aspect of your genetic algorithm. You also created a mechanism for tracking the genealogy of your evolution. Remember, genealogy is a family tree of the entire evolution.

In this section, you’ll learn how to export the genealogy tree and visualize it using a third-party tool.

Start by opening tiger_simulation.exs. At the moment, it looks like this:

​ ​defmoduleTigerSimulationdo​
​   @behaviour Problemalias Types.Chromosome
​ 
​   @impl true
​   ​defgenotypedo​
​     genes = for _ <- 1..8, ​do​: Enum.random(0..1)
​     %Chromosome{​genes:​ genes, ​size:8}
​   ​end​
​ 
​   @impl true
​   ​deffitness_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(tundra_scores)
​     |> Enum.map(​fn​ {t, s} -> t*s ​end​)
​     |> Enum.sum()
​   ​end​
​ 
​   @impl true
​   ​defterminate?(_population, generation), ​do​: generation == 1000
​ 
​   ​defaverage_tiger(population) ​do​
​     genes = Enum.map(population, & &1.genes)
​     fitnesses = Enum.map(population, & &1.fitness)
​     ages = Enum.map(population, & &1.age)
​     num_tigers = length(population)
​ 
​     avg_fitness = Enum.sum(fitnesses) / num_tigers
​     avg_age = Enum.sum(ages) / num_tigers
​     avg_genes =
​       genes
​       |> Enum.zip()
​       |> Enum.map(& Enum.sum(Tuple.to_list(&1)) / num_tigers)
​ 
​     %Chromosome{​genes:​ avg_genes, ​age:​ avg_age, ​fitness:​ avg_fitness}
​   ​end​
​ ​end​
​ 
​ tiger = Genetic.run(TigerSimulation,
​                     ​population_size:20,
​                     ​selection_rate:0.9,
​                     ​mutation_rate:0.1,
​                     ​statistics:​
​                       %{​average_tiger:​ &TigerSimulation.average_tiger/1})
​ 
​ IO.write(​"​​\n"​)
​ IO.inspect(tiger)
​ genealogy = Utilities.Genealogy.get_tree()
​ 
​ IO.inspect(Graph.vertices(genealogy))

You’ll need to start by adjusting the termination criteria. Currently, the algorithm is set to terminate after 1000 generations. For the purposes of this chapter, you’ll decrease this number significantly to make it easier to visualize the genealogy. In this example, you’ll decrease the number of generations to 1 and the population size to 5:

​ ​defmodule​ TigerSimulation ​do​
​   ​# ...​
​   ​def​ terminate?(_population, generation), ​do​: generation == 0
​   ​# ...​
​ ​end​
​ 
​ tiger = Genetic.run(TigerSimulation,
​                     ​population_size:​ 2,
​                     ​selection_rate:​ 1.0,
​                     ​mutation_rate:​ 0.0,
​                     ​statistics:​
​                       %{​average_tiger:​ &TigerSimulation.average_tiger/1})

Next, you need to export your genealogy tree to a DOT file, like this:

​ 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)

In this snippet, you serialize the genealogy tree to a DOT binary using the libgraph API. DOT is a standard serialized graph format. You then create a new file and write the serialized graph to it.

When you run your algorithm, you’ll see tiger_simulation.dot in your main directory. Now, to visualize, you can download Graphviz[6] or navigate to Webgraphviz[7] and copy the contents of tiger_simulation.dot into the text field. Your genealogy tree will end up looking something like the image.

Notice the genealogy is essentially a tree starting from 2 chromosomes in the first generation and spanning out from there. This type of visualization is excellent in showing how the fittest chromosome transformed over time.

👈 Chapter 10 Visualizing the Results | TOC | Visualizing Basic Statistics 👉

Genetic Algorithms in Elixir by Sean Moriarity can be purchased in other book formats directly from the Pragmatic Programmers. If you notice a code error or formatting mistake, please let us know here so that we can fix it.

Smgaelixir
Recommended from ReadMedium