avatarThe Pragmatic Programmers

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

2862

Abstract

d="ec7e">Stream.repeatedly/1 returns a Stream that will continuously apply a function. In this case, the function you want it to apply is Enum.random/1 over every single alphabetical character. You then take 34 different characters from the stream to produce solutions.</p><p id="61ce">The next thing you need is a fitness function. You want your fitness function to be a measure of how close the guessed word is to the target word. Luckily, Elixir has some functions that implement the desired behavior. Implement your fitness function like this:</p><div id="7cbd"><pre>​ ​def​ <span class="hljs-built_in">fitness_function</span>(chromosome) ​<span class="hljs-keyword">do</span>​ ​ target = ​<span class="hljs-string">"​​supercalifragilisticexpialidocious"</span>​ ​ guess = List.<span class="hljs-built_in">to_string</span>(chromosome.genes) ​ <span class="hljs-type">String</span>.<span class="hljs-built_in">jaro_distance</span>(target, guess) ​ ​end​</pre></div><p id="2d6c">String.jaro_distance/2 returns the similarity between the two words, with 1 meaning the words are the same. You need to convert your genes to a string because String.jaro_distance/2 expects two Strings.</p><p id="9cc6">Because String.jaro_distance/2 only returns 1 when the words are the same, you can implement your termination criteria like this:</p><div id="f9f9"><pre>​ ​def​ <span class="hljs-keyword">terminate</span>?([best | _]), ​<span class="hljs-keyword">do</span>​: best.fitness == <span class="hljs-number">1</span></pre></div><p id="7e0e">That’s all you need. Your whole problem should look like:</p><div id="3f4c"><pre>​ ​<span class="hljs-class"><span class="hljs-keyword">defmodule</span><span class="hljs-title">Speller</span></span><span class="hljs-keyword">do</span>​ ​ <span class="hljs-variable">@behaviour</span> <span class="hljs-title class_">Problem</span><span class="hljs-keyword">alias</span> <span class="hljs-title class_">Types</span>.<span class="hljs-title class_">Chromosome</span> ​ ​ ​<span class="hljs-function"><span class="hljs-keyword">def</span><span class="hljs-title">genotype</span></span><span class="hljs-keyword">do</span>​ ​ genes = ​ <span class="hljs-title class_">Stream</span>.repeatedly(​<span class="hljs-keyword">fn</span>​ -> <span class="hljs-title class_">Enum</span>.random(​?a​..​?z​) ​<span class="hljs-keyword">end</span>​) ​ |> <span class="hljs-title class_">Enum</span>.take(<span class="hljs-number">34</span>) ​ %<span class="hljs-title class_">Chromosome</span>{​<span class="hljs-symbol">genes:</span>​ genes, ​<span class="hljs-symbol">size:</span><span class="hljs-number">34</span>} ​ ​<span class="hljs-keyword">end</span>​ ​ ​ ​<span class="hljs-function"><span class="hljs-keyword">def</span><span class="hljs-title">fitness_function</span></spa

Options

n>(chromosome) ​<span class="hljs-keyword">do</span>​ ​ target = ​<span class="hljs-string">'supercalifragilisticexpialidocious'</span>​ ​ guess = chromosome.genes ​ <span class="hljs-title class_">String</span>.jaro_distance(target, guess) ​ ​<span class="hljs-keyword">end</span>​ ​ ​ ​<span class="hljs-function"><span class="hljs-keyword">def</span><span class="hljs-title">terminate?</span></span>([best | _]), ​<span class="hljs-keyword">do</span>​: best.fitness == <span class="hljs-number">1</span> ​ ​<span class="hljs-keyword">end</span></pre></div><p id="f0df">Now, add the following below your module:</p><div id="35c9"><pre>​ soln = Genetic<span class="hljs-selector-class">.run</span>(Speller) ​ ​ IO<span class="hljs-selector-class">.write</span>(​<span class="hljs-string">"​​\n"</span>​) ​ IO<span class="hljs-selector-class">.inspect</span>(soln)</pre></div><p id="70d3">Now run your algorithm:</p><div id="b4d6"><pre>​ ​$ ​​mix​​ ​​<span class="hljs-keyword">run</span><span class="language-bash">​​ ​​scripts/speller.exs​</span> ​ Current Best: <span class="hljs-number">1.0</span> ​ supercalifragilisticexpialidocious</pre></div><p id="fe4b">That’s all it takes. Spelling a word you can just look up might not be the most impressive task; however, this problem shows the power of your framework. All you need to run a genetic algorithm against a problem is to define a genotype, fitness function, and termination criteria.</p><h1 id="cc49">“It’s Taking Too Long…”</h1><h2 id="5c4c">INFORMATION</h2><p id="537f">You might find that your algorithm in the last problem takes awhile to converge. That’s OK. You haven’t been equipped with the necessary tools to write algorithms that converge quickly on more difficult problems like this one. In the next few chapters, you’ll add some tools that will make algorithms like this one a breeze.</p><p id="aa1b">As you continue through this book, you’ll use this same Problem abstraction to solve progressively more difficult problems.</p><p id="4074"><i>👈 <a href="https://readmedium.com/solving-one-max-for-the-last-time-891a1a0fb142">Solving One-Max for the Last Time</a> | <a href="https://readmedium.com/table-of-contents-879fc8614df">TOC</a> | <a href="https://readmedium.com/what-you-learned-7329fea98eb8">What You Learned</a> 👉</i></p><p id="d45c"><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="4da6"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*OZt5jtL90ZqYviPotaxYkQ.jpeg"><figcaption></figcaption></figure></article></body>

Spelling Words with Genetic Algorithms

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

👈 Solving One-Max for the Last Time | TOC | What You Learned 👉

To illustrate the power of your new framework, you’ll use it to solve a new basic problem: spelling. You’ll teach your algorithm to spell an impossibly long word: supercalifragilisticexpialidocious.

Start by creating a new file called speller.exs in scripts and define a new problem:

​ ​defmoduleSpellerdo​
​   @behaviour Problemalias Types.Chromosome
​   ​defgenotype, ​do​: ​# ...​
​ 
​   ​deffitness_function(chromosome), ​do​: ​# ...​
​ 
​   ​defterminate?(population), ​do​: ​# ...​
​ 
​ ​end

Now you need to define a Problem implementation for your tasks. In this chapter, you learned there are three parts to every problem: a genotype, a fitness function, and termination criteria.

The first thing you’ll want to decide is the genotype. Your goal is to spell supercalifragilisticexpialidocious which is a 34-letter word. That means your search space is all 34-letter words. You can define your genotype like this:

​ ​def​ genotype ​do​
​   genes =
​     Stream.repeatedly(​fn​ -> Enum.random(​?a​..​?z​) ​end​)
​     |> Enum.take(34)
​   %Chromosome{​genes:​ genes, ​size:​ 34}
​ ​end

Stream.repeatedly/1 returns a Stream that will continuously apply a function. In this case, the function you want it to apply is Enum.random/1 over every single alphabetical character. You then take 34 different characters from the stream to produce solutions.

The next thing you need is a fitness function. You want your fitness function to be a measure of how close the guessed word is to the target word. Luckily, Elixir has some functions that implement the desired behavior. Implement your fitness function like this:

​ ​def​ fitness_function(chromosome) ​do​
​   target = ​"​​supercalifragilisticexpialidocious"​
​   guess = List.to_string(chromosome.genes)
​   String.jaro_distance(target, guess)
​ ​end​

String.jaro_distance/2 returns the similarity between the two words, with 1 meaning the words are the same. You need to convert your genes to a string because String.jaro_distance/2 expects two Strings.

Because String.jaro_distance/2 only returns 1 when the words are the same, you can implement your termination criteria like this:

​ ​def​ terminate?([best | _]), ​do​: best.fitness == 1

That’s all you need. Your whole problem should look like:

​ ​defmoduleSpellerdo​
​   @behaviour Problemalias Types.Chromosome
​ 
​   ​defgenotypedo​
​     genes =
​       Stream.repeatedly(​fn​ -> Enum.random(​?a​..​?z​) ​end​)
​       |> Enum.take(34)
​     %Chromosome{​genes:​ genes, ​size:34}
​   ​end​
​ 
​   ​deffitness_function(chromosome) ​do​
​     target = ​'supercalifragilisticexpialidocious'​
​     guess = chromosome.genes
​     String.jaro_distance(target, guess)
​   ​end​
​ 
​   ​defterminate?([best | _]), ​do​: best.fitness == 1
​ ​end

Now, add the following below your module:

​ soln = Genetic.run(Speller)
​ 
​ IO.write(​"​​\n"​)
​ IO.inspect(soln)

Now run your algorithm:

​ ​$ ​​mix​​ ​​run​​ ​​scripts/speller.exs​
​ Current Best: 1.0
​ supercalifragilisticexpialidocious

That’s all it takes. Spelling a word you can just look up might not be the most impressive task; however, this problem shows the power of your framework. All you need to run a genetic algorithm against a problem is to define a genotype, fitness function, and termination criteria.

“It’s Taking Too Long…”

INFORMATION

You might find that your algorithm in the last problem takes awhile to converge. That’s OK. You haven’t been equipped with the necessary tools to write algorithms that converge quickly on more difficult problems like this one. In the next few chapters, you’ll add some tools that will make algorithms like this one a breeze.

As you continue through this book, you’ll use this same Problem abstraction to solve progressively more difficult problems.

👈 Solving One-Max for the Last Time | TOC | What You Learned 👉

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