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:
defmodule Toolbox.Mutation do
alias Types.Chromosome
# ...
endYou’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:
def mutation(population, opts \\ []) do
population
|> Enum.map(
fn chromosome ->
if :rand.uniform() < 0.05 do
%Chromosome{genes: Enum.shuffle(chromosome.genes)}
else
chromosome
end
end
)
endRemember, 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.05 do
» 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:
def mutation(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.







