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

lirux
Hi Jamis, I think there’s an issue with a test on chapter 6. I own the ebook, version P1.0 Feb. 2019. This test doesn’t pass for me: ...
New
raul
Hi Travis! Thank you for the cool book! :slight_smile: I made a list of issues and thought I could post them chapter by chapter. I’m rev...
New
joepstender
The generated iex result below should list products instead of product for the metadata. (page 67) iex&gt; product = %Product{} %Pento....
New
leba0495
Hello! Thanks for the great book. I was attempting the Trie (chap 17) exercises and for number 4 the solution provided for the autocorre...
New
fynn
This is as much a suggestion as a question, as a note for others. Locally the SGP30 wasn’t available, so I ordered a SGP40. On page 53, ...
New
adamwoolhether
I’m not quite sure what’s going on here, but I’m unable to have to containers successfully complete the Readiness/Liveness checks. I’m im...
New
hazardco
On page 78 the following code appears: &lt;%= link_to ‘Destroy’, product, class: ‘hover:underline’, method: :delete, data: { confirm...
New
mert
AWDWR 7, page 152, page 153: Hello everyone, I’m a little bit lost on the hotwire part. I didn’t fully understand it. On page 152 @rub...
New
EdBorn
Title: Agile Web Development with Rails 7: (page 70) I am running windows 11 pro with rails 7.0.3 and ruby 3.1.2p20 (2022-04-12 revision...
New
Keton
When running the program in chapter 8, “Implementing Combat”, the printout Health before attack was never printed so I assumed something ...
New

Other popular topics Top

AstonJ
A thread that every forum needs! Simply post a link to a track on YouTube (or SoundCloud or Vimeo amongst others!) on a separate line an...
New
AstonJ
What chair do you have while working… and why? Is there a ‘best’ type of chair or working position for developers?
New
dasdom
No chair. I have a standing desk. This post was split into a dedicated thread from our thread about chairs :slight_smile:
New
AstonJ
You might be thinking we should just ask who’s not using VSCode :joy: however there are some new additions in the space that might give V...
New
AstonJ
Continuing the discussion from Thinking about learning Crystal, let’s discuss - I was wondering which languages don’t GC - maybe we can c...
New
AstonJ
If you get Can't find emacs in your PATH when trying to install Doom Emacs on your Mac you… just… need to install Emacs first! :lol: bre...
New
PragmaticBookshelf
Programming Ruby is the most complete book on Ruby, covering both the language itself and the standard library as well as commonly used t...
New
DevotionGeo
I have always used antique keyboards like Cherry MX 1800 or Cherry MX 8100 and almost always have modified the switches in some way, like...
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
PragmaticBookshelf
Use advanced functional programming principles, practical Domain-Driven Design techniques, and production-ready Elixir code to build scal...
New

Sub Categories: