tvanderpol

tvanderpol

Genetic Algorithms in Elixir: First stab at Ch1 algorithm converges just fine? (p28)

I’ve got something of the opposite of the usual problem - my code doesn’t fail in the way the text suggests it should (when it suggests premature convergence is the cause).

I’ve ran the code a fair bit and did some debug print injecting and such but I can’t really see anything out of the ordinary - it converges on the correct answer almost instantly if I run it without any additional debug and it takes a reasonable amount of generations from what I can tell by poking at it.

Now I am clear to continue the chapter as-is, and I understand the argument being made, but I don’t understand how the algorithm is meant to fail (I do in the abstract but I mean the code as written) and that’s bugging me.

Marked As Solved

seanmor5

seanmor5

Author of Genetic Algorithms in Elixir

There was a mistake in my version of the code that forced early convergence with smaller populations that isn’t present in the book’s transcription of the code.

To better demonstrate premature convergence: set chromosome size to 1000 and population size to 100. You’ll notice your version without mutation converges much slower than with mutation. You can continue to decrease the population size further and further and you’ll reach a point where progress completely stops.

Sorry about the confusion!

Also Liked

christhekeele

christhekeele

This was on quite a new Macbook, which may be influencing results from what’s expected.

MacBook Pro (16-inch, 2019)
2.4 GHz 8-Core Intel Core i9
32 GB 2667 MHz DDR4
Erlang/OTP 22 [erts-10.7.2] [64-bit] [smp:16:16]
Elixir 1.10.3

tvanderpol

tvanderpol

Perfect, thank you for responding so fast! That helps my understanding of exactly how it fails a lot.

christhekeele

christhekeele

I noticed this in the B1 edition as well!

Re:

$ elixir one_max.exs
Current Best: 32

But wait, what’s going on here? Why is the algorithm stopping on a best fitness below 42? No matter how many times you run it, the algorithm will almost always certainly stop improving below 42. The problem is premature convergence.

At this point, the chapter’s example code looks like:

Code to Date
population = for _ <- 1..10, do: for _ <- 1..42, do: Enum.random(0..1)

evaluate = fn population ->
  Enum.sort_by(population, &Enum.sum(&1), &>=/2)
end

selection = fn population ->
  population
  |> Enum.chunk_every(2)
  |> Enum.map(&List.to_tuple(&1))
end

crossover = fn population ->
  Enum.reduce(population, [], fn {p1, p2}, acc ->
    cx_point = :rand.uniform(42)
    {{h1, t1}, {h2, t2}} = {Enum.split(p1, cx_point), Enum.split(p2, cx_point)}
    [h1 ++ t2 | [h2 ++ t1 | acc]]
  end)
end

algorithm = fn population, algorithm ->
  best = Enum.max_by(population, &Enum.sum(&1))
  IO.write("\rCurrent Best: " <> Integer.to_string(Enum.sum(best)))
  if Enum.sum(best) == 42 do
    best
  else
    population
    |> evaluate.()
    |> selection.()
    |> crossover.()
    |> algorithm.(algorithm)
  end
end

solution = algorithm.(population, algorithm)
IO.write("\n Answer is \n")
IO.inspect solution

I parameterized it thusly:

Tunable version
-population = for _ <- 1..10, do: for _ <- 1..42, do: Enum.random(0..1)
+problem_size = 42
+population_size = 100
+
+population = for _ <- 1..population_size, do: for _ <- 1..problem_size, do: Enum.random(0..1)

 evaluate = fn population ->
   Enum.sort_by(population, &Enum.sum(&1), &>=/2)
 end

 selection = fn population ->
   population
   |> Enum.chunk_every(2)
   |> Enum.map(&List.to_tuple(&1))
 end

 crossover = fn population ->
   Enum.reduce(population, [], fn {p1, p2}, acc ->
-    cx_point = :rand.uniform(42)
+    cx_point = :rand.uniform(problem_size)
     {{h1, t1}, {h2, t2}} = {Enum.split(p1, cx_point), Enum.split(p2, cx_point)}
     [h1 ++ t2 | [h2 ++ t1 | acc]]
   end)
 end

 algorithm = fn population, algorithm ->
   best = Enum.max_by(population, &Enum.sum(&1))
   IO.write("\rCurrent Best: " <> Integer.to_string(Enum.sum(best)))
-  if Enum.sum(best) == 42 do
+  if Enum.sum(best) == problem_size do
     best
   else
     population
     |> evaluate.()
     |> selection.()
     |> crossover.()
     |> algorithm.(algorithm)
   end
 end

 solution = algorithm.(population, algorithm)
 IO.write("\n Answer is \n")
 IO.inspect solution

In my experimentation, with problem_size = 42, not only did population_size = 100 always converge on the best answer, but even as low as population_size = 8 consistently converged. I started seeing the need for mutation around population_size = 6, which normally gets stuck around 35.

Alternatively, increasing the size of the problem to problem_size = 420 usually converged correctly, but with enough time to watch things work. problem_size = 4200 consistently gets stuck around 2400, as the narrative of the chapter wants it to.

Where Next?

Popular Pragmatic Bookshelf topics Top

jimmykiang
This test is broken right out of the box… — FAIL: TestAgent (7.82s) agent_test.go:77: Error Trace: agent_test.go:77 agent_test.go:...
New
ianwillie
Hello Brian, I have some problems with running the code in your book. I like the style of the book very much and I have learnt a lot as...
New
jesse050717
Title: Web Development with Clojure, Third Edition, pg 116 Hi - I just started chapter 5 and I am stuck on page 116 while trying to star...
New
sdmoralesma
Title: Web Development with Clojure, Third Edition - migrations/create not working: p159 When I execute the command: user=&gt; (create-...
New
JohnS
I can’t setup the Rails source code. This happens in a working directory containing multiple (postgres) Rails apps. With: ruby-3.0.0 s...
New
Chrichton
Dear Sophie. I tried to do the “Authorization” exercise and have two questions: When trying to plug in an email-service, I found the ...
New
jskubick
I found an issue in Chapter 7 regarding android:backgroundTint vs app:backgroundTint. How to replicate: load chapter-7 from zipfile i...
New
brunogirin
When installing Cards as an editable package, I get the following error: ERROR: File “setup.py” not found. Directory cannot be installe...
New
brunogirin
When running tox for the first time, I got the following error: ERROR: InterpreterNotFound: python3.10 I realised that I was running ...
New
kolossal
Hi, I need some help, I’m new to rust and was learning through your book. but I got stuck at the last stage of distribution. Whenever I t...
New

Other popular topics Top

ohm
Which, if any, games do you play? On what platform? I just bought (and completed) Minecraft Dungeons for my Nintendo Switch. Other than ...
New
axelson
I’ve been really enjoying obsidian.md: It is very snappy (even though it is based on Electron). I love that it is all local by defaul...
New
Exadra37
On modern versions of macOS, you simply can’t power on your computer, launch a text editor or eBook reader, and write or read, without a ...
New
PragmaticBookshelf
“Finding the Boundaries” Hero’s Journey with Noel Rappin @noelrappin Even when you’re ultimately right about what the future ho...
New
AstonJ
Seems like a lot of people caught it - just wondered whether any of you did? As far as I know I didn’t, but it wouldn’t surprise me if I...
New
AstonJ
Saw this on TikTok of all places! :lol: Anyone heard of them before? Lite:
New
mafinar
This is going to be a long an frequently posted thread. While talking to a friend of mine who has taken data structure and algorithm cou...
New
PragmaticBookshelf
Author Spotlight: Sophie DeBenedetto @SophieDeBenedetto The days of the traditional request-response web application are long gone, b...
New
AstonJ
If you’re getting errors like this: psql: error: connection to server on socket “/tmp/.s.PGSQL.5432” failed: No such file or directory ...
New
AstonJ
Curious what kind of results others are getting, I think actually prefer the 7B model to the 32B model, not only is it faster but the qua...
New

Sub Categories: