genetic

genetic algorithm framework
git clone igris.git:dracuxan/genetic.git
Log | Files | Refs | README

commit 47bc5d08c726253078ee45d20479ddaa26353549
parent e700eba136d4dabc9a9bbe120247e78bb2f913b5
Author: dracuxan <[email protected]>
Date:   Wed,  8 Apr 2026 17:07:05 +0530

new: custom mutation methods

Diffstat:
Mgenetic/lib/genetic.ex | 9++++++---
Agenetic/lib/toolbox/mutation.ex | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgenetic/scripts/codebreaker.exs | 33++++++++++++++++++---------------
Mgenetic/scripts/one_max.exs | 3++-
4 files changed, 94 insertions(+), 19 deletions(-)

diff --git a/genetic/lib/genetic.ex b/genetic/lib/genetic.ex @@ -66,11 +66,14 @@ defmodule Genetic do ) end - def mutation(population, _opts \\ []) do + def mutation(population, opts \\ []) do + mutation_fn = Keyword.get(opts, :mutation_type, &Toolbox.Mutation.gaussian/2) + mutation_rate = Keyword.get(opts, :mutation_rate, 0.05) + population |> Enum.map(fn chromosome -> - if :rand.uniform() < 0.05 do - %Chromosome{chromosome | genes: Enum.shuffle(chromosome.genes)} + if :rand.uniform() < mutation_rate do + apply(mutation_fn, [chromosome, opts]) else chromosome end diff --git a/genetic/lib/toolbox/mutation.ex b/genetic/lib/toolbox/mutation.ex @@ -0,0 +1,68 @@ +defmodule Toolbox.Mutation do + alias Types.Chromosome + + def flip(chromosome, _opts \\ []) do + genes = + chromosome.genes + |> Enum.map(&Bitwise.bxor(&1, 1)) + + %Chromosome{genes: genes, size: chromosome.size} + end + + def flip_some(chromosome, opts \\ []) do + p = Keyword.get(opts, :p, 0.5) + + genes = + chromosome.genes + |> Enum.map(fn g -> + if :rand.uniform() < p do + Bitwise.bxor(g, 1) + else + g + end + end) + + %Chromosome{genes: genes, size: chromosome.size} + end + + def scramble(chromosome, _opts \\ []) do + genes = + chromosome.genes + |> Enum.shuffle() + + %Chromosome{genes: genes, size: chromosome.size} + end + + def scramble_n(chromosome, _opts \\ []) do + n = 10 + start = :rand.uniform(n - 1) + + {lo, hi} = + if start + n >= chromosome.size do + {start - n, start} + else + {start, start + n} + end + + head = Enum.slice(chromosome.genes, 0, lo) + mid = Enum.slice(chromosome.genes, lo, hi) |> Enum.shuffle() + tail = Enum.slice(chromosome.genes, hi, chromosome.size) + %Chromosome{genes: head ++ mid ++ tail, size: chromosome.size} + end + + def gaussian(chromosome, _opts \\ []) do + mu = Enum.sum(chromosome.genes) / length(chromosome.genes) + + sigma = + chromosome.genes + |> Enum.map(fn x -> (mu - x) * (mu - x) end) + |> Enum.sum() + |> Kernel./(length(chromosome.genes)) + + genes = + chromosome.genes + |> Enum.map(fn _ -> :rand.normal(mu, sigma) end) + + %Chromosome{genes: genes, size: chromosome.size} + end +end diff --git a/genetic/scripts/codebreaker.exs b/genetic/scripts/codebreaker.exs @@ -10,8 +10,8 @@ defmodule Codebreaker do end def fitness_function(chromosome) do - target = "ILoveGeneticAlgorithms" - encrypted = "LIjs`B`k`qlfDibjwlqmhv" + target = "dracuxan" + encrypted = "awdfp}dk" cypher = fn word, key -> word @@ -19,12 +19,7 @@ defmodule Codebreaker do |> Enum.map(fn x -> rem(bxor(x, key), 32768) end) end - key = - chromosome.genes - |> Enum.map(&Integer.to_string(&1)) - |> Enum.join("") - |> String.to_integer(2) - + key = generate_integer_key(chromosome.genes) guess = List.to_string(cypher.(encrypted, key)) String.jaro_distance(target, guess) end @@ -33,14 +28,22 @@ defmodule Codebreaker do best = hd(population) best.fitness == 1 end + + def generate_integer_key(key) do + key + |> Enum.map(&Integer.to_string(&1)) + |> Enum.join("") + |> String.to_integer(2) + end end -{soln, _} = Genetic.run(Codebreaker, crossover_type: &Toolbox.Crossover.single_point/3) +{soln, generation} = + Genetic.run( + Codebreaker, + crossover_type: &Toolbox.Crossover.single_point/3, + mutation_type: &Toolbox.Mutation.flip_some/2 + ) -{key, ""} = - soln.genes - |> Enum.map(&Integer.to_string(&1)) - |> Enum.join("") - |> Integer.parse(2) +key = Codebreaker.generate_integer_key(soln.genes) -IO.puts("\nThe key is #{key}") +IO.puts("\nkey: #{key}\ngenerations passed: #{generation}") diff --git a/genetic/scripts/one_max.exs b/genetic/scripts/one_max.exs @@ -38,7 +38,8 @@ end Genetic.run( OneMax, selection_type: &Toolbox.Selection.elite/2, - crossover_type: &Toolbox.Crossover.uniform/3 + crossover_type: &Toolbox.Crossover.uniform/3, + mutation_type: &Toolbox.Mutation.gaussian/2 ) IO.puts("\nfinal answer: #{inspect(soln)}")