avatarThe Pragmatic Programmers

Summary

The provided content outlines the process of customizing mutation operations within a genetic algorithm framework in Elixir, as described by Sean Moriarity.

Abstract

The web content discusses the customization of mutation hyperparameters in genetic algorithms using Elixir. It guides the reader through creating a mutation toolbox, similar to those for selection and crossover, by introducing a new module called Toolbox.Mutation. The article explains how to implement mutation strategies and adjust mutation rates within the genetic algorithm framework. It provides code snippets for defining mutation functions, extracting mutation strategies and rates from options, and applying these strategies to chromosomes in a population with a probability corresponding to the mutation rate. The content also mentions the default mutation strategy, flip mutation, and suggests a default mutation rate of 5%. The article is part of a series on genetic algorithms in Elixir and includes links to related chapters and the option for readers to report errata.

Opinions

  • The author, Sean Moriarity, emphasizes the importance of customizing mutation operations to tailor genetic algorithms to specific needs.
  • The default mutation rate of 5% is presented as a good starting point, but the framework allows for easy adjustment to suit different scenarios.
  • The use of a mutation toolbox and the ability to pass options to the algorithm when calling Genetic.run/2 demonstrate a flexible and modular approach to implementing genetic algorithms in Elixir.
  • The article assumes a level of familiarity with Elixir and genetic algorithms, targeting readers who have progressed through previous chapters on selection and crossover.
  • By providing a structure for implementing common mutation strategies, the author encourages experimentation and exploration of different mutation approaches to optimize algorithm performance.

Customizing Mutation in Your Framework

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

👈 Understanding Mutation | TOC | Implementing Common Mutation Strategies 👉

Just like in Chapter 5, Selecting the Best, and Chapter 6, Generating New Solutions, you’ll need to slightly modify your framework to allow you to customize mutation hyperparameters. These hyperparameters are mutation strategy and mutation rate.

In this section, you’ll create a mutation toolbox and modify your framework to allow you to easily customize mutation in your algorithms.

Creating the Toolbox

First, you need to create a mutation toolbox, just like you created a selection and crossover toolbox. Create a new file in toolbox called mutation.ex.

Next, create the Toolbox.Mutation module, like this:

​ ​defmoduleToolbox.Mutationdo​
​   alias Types.Chromosome
​ 
​   ​# ...​
​ ​end

You’ll be working a lot with the Chromosome struct in this module, so you’ll want to create an alias. Now, whenever you implement a new mutation strategy, you’ll add it to your mutation toolbox.

Changing Mutation Strategy

Open up genetic.ex and navigate to the mutation/2 function. It looks like this:

​ ​defmutation(population, opts \\ []) ​do​
​   population
​   |> Enum.map(
​       ​fn​ chromosome ->
​         ​if​ ​:rand​.uniform() < 0.05do​
​           %Chromosome{​genes:Enum.shuffle(chromosome.genes)}
​         ​else​
​           chromosome
​         ​end​
​       ​end​
​     )
​ ​end

Remember, opts is a Keyword representing the options you can pass to your algorithm when you call Genetic.run/2. First, you need to extract a mutation strategy from opts, like this:

​ mutate_fn = Keyword.get(opts, ​:mutation_type​, &Toolbox.Mutation.flip/1)

Right now, the default is called flip mutation, which is a mutation strategy you’ll implement in the next section. You can change this default to another method if you prefer.

Next, you need to apply your mutation strategy to chromosomes in the population. Change the body of Enum.map/2 to look like this:

​ |> Enum.map(
​     ​fn​ chromosome ->
​       ​if​ ​:rand​.uniform() < 0.05do​
»        apply(mutate_fn, [chromosome])
​       ​else​
​         chromosome
​       ​end​
​     )

Here you use apply/2 to apply your extracted mutation strategy to chromosome. The mutation strategies you’ll implement in this chapter accept a chromosome and return the mutated chromosome, so apply/2 returns a mutated version of chromosome.

Adjusting Mutation Rate

You also need a way to control the mutation rate of your algorithm. Start by extracting :mutation_rate from opts:

​ mutate_fn = Keyword.get(opts, ​:mutation_type​, &Toolbox.Mutation.flip/1)
​ rate = Keyword.get(opts, ​:mutation_rate​, 0.05)

0.05 represents a mutation rate of 5%, which is a good default.

Next, you need to change Enum.map/2 to only mutate chromosomes according to rate. To do this, change the if-condition to look like this:

​ |> Enum.map(
​     ​fn​ chromosome ->
»      ​if​ ​:rand​.uniform() < rate ​do​
​         apply(mutate_fn, [chromosome])
​       ​else​
​         chromosome
​       ​end​
​     )

Your new mutation/2 function should look like this:

​ ​defmutation(population, opts \\ []) ​do​
»  mutate_fn = Keyword.get(opts, ​:mutation_type​, &Toolbox.Mutation.scramble/1)
»  rate = Keyword.get(opts, ​:mutation_rate​, 0.05)
​   population
​   |> Enum.map(
​       ​fn​ chromosome ->
»        ​if​ ​:rand​.uniform() < rate ​do​
»          apply(mutate_fn, [chromosome])
​         ​else​
​           chromosome
​         ​end​
​       ​end​
​     )
​ ​end

👈 Understanding Mutation | TOC | Implementing Common Mutation Strategies 👉

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