genetic

genetic algorithm framework
git clone [email protected]:dracuxan/genetic.git
Log | Files | Refs | README

crossover.ex (3568B)


      1 defmodule Toolbox.Crossover do
      2   alias Types.Chromosome
      3 
      4   def order_one(p1, p2, _opts \\ []) do
      5     lim = Enum.count(p1.genes) - 1
      6     # random range
      7     {i1, i2} =
      8       [:rand.uniform(lim), :rand.uniform(lim)]
      9       |> Enum.sort()
     10       |> List.to_tuple()
     11 
     12     # p2 contribution
     13     slice1 = Enum.slice(p1.genes, i1..i2)
     14     slice1_set = MapSet.new(slice1)
     15     p2_contrib = Enum.reject(p2.genes, &MapSet.member?(slice1_set, &1))
     16     {head1, tail1} = Enum.split(p2_contrib, i1)
     17 
     18     # p1 contribution
     19     slice2 = Enum.slice(p2.genes, i1..i2)
     20     slice2_set = MapSet.new(slice2)
     21     p1_contrib = Enum.reject(p1.genes, &MapSet.member?(slice2_set, &1))
     22     {head2, tail2} = Enum.split(p1_contrib, i1)
     23 
     24     # create and return
     25     {c1, c2} = {head1 ++ slice1 ++ tail1, head2 ++ slice2 ++ tail2}
     26 
     27     {
     28       %Chromosome{genes: c1, size: length(c1), age: 0},
     29       %Chromosome{genes: c2, size: length(c2), age: 0}
     30     }
     31   end
     32 
     33   def single_point(p1, p2, _opts \\ []) do
     34     cx_point = rem(Genetic.Random.xor96(), length(p1.genes))
     35 
     36     {{head1, tail1}, {head2, tail2}} =
     37       {Enum.split(p1.genes, cx_point), Enum.split(p2.genes, cx_point)}
     38 
     39     {c1, c2} = {head1 ++ tail2, head2 ++ tail1}
     40 
     41     {
     42       %Chromosome{genes: c1, size: length(c1), age: 0},
     43       %Chromosome{genes: c2, size: length(c2), age: 0}
     44     }
     45   end
     46 
     47   def uniform(p1, p2, opts \\ []) do
     48     rate = Keyword.get(opts, :rate, 0.5)
     49 
     50     {c1, c2} =
     51       p1.genes
     52       |> Enum.zip(p2.genes)
     53       |> Enum.map(fn {x, y} ->
     54         if :rand.uniform() < rate do
     55           {x, y}
     56         else
     57           {y, x}
     58         end
     59       end)
     60       |> Enum.unzip()
     61 
     62     {
     63       %Chromosome{genes: c1, size: length(c1), age: 0},
     64       %Chromosome{genes: c2, size: length(c2), age: 0}
     65     }
     66   end
     67 
     68   def blend(p1, p2, opts \\ []) do
     69     p1_1 = Enum.at(p1.genes, 0)
     70     p2_1 = Enum.at(p2.genes, 0)
     71     alpha = Keyword.get(opts, :alpha, 1)
     72     {min, max} = {Keyword.get(opts, :min, -10), Keyword.get(opts, :max, 10)}
     73     shift = (0.1 + 0.2 * alpha) * :rand.uniform() - alpha
     74     c1 = (0.1 - shift) * p1_1 + shift * p2_1
     75     c2 = shift * p1_1 + (0.1 - shift) * p2_1
     76 
     77     {
     78       %Chromosome{genes: [utils_constraints(c1, min, max)], size: 1, age: 0},
     79       %Chromosome{genes: [utils_constraints(c2, min, max)], size: 1, age: 0}
     80     }
     81   end
     82 
     83   defp utils_constraints(p, min, max) do
     84     p = if p > max, do: max, else: p
     85     p = if p < min, do: min, else: p
     86     p
     87   end
     88 
     89   def whole_arethmetic(p1, p2, opts \\ []) do
     90     alpha = Keyword.get(opts, :alpha, 0.5)
     91 
     92     {c1, c2} =
     93       p1.genes
     94       |> Enum.zip(p2.genes)
     95       |> Enum.map(fn {x, y} ->
     96         {
     97           x * alpha + y * (1 - alpha),
     98           x * (alpha - 1) + y * alpha
     99         }
    100       end)
    101       |> Enum.unzip()
    102 
    103     {
    104       %Chromosome{genes: c1, size: length(c1), age: 0},
    105       %Chromosome{genes: c2, size: length(c2), age: 0}
    106     }
    107   end
    108 
    109   # demo of how crossover can be performed on mulitple parents at once
    110   def single_point_multi(parents, opts \\ [])
    111   def single_point_multi([], _opts), do: raise("more than one parent needed!")
    112   def single_point_multi([p1 | []], _opts), do: p1
    113 
    114   def single_point_multi(parents, _opts) do
    115     p1 = hd(parents)
    116     cx_point = :rand.uniform(p1.size)
    117 
    118     parents
    119     |> Enum.chunk_every(2, 1, [hd(parents)])
    120     |> Enum.map(&List.to_tuple(&1))
    121     |> Enum.reduce(
    122       [],
    123       fn {p1, p2}, chd ->
    124         {front, _} = Enum.split(p1.genes, cx_point)
    125         {_, back} = Enum.split(p2.genes, cx_point)
    126         c = %Chromosome{genes: front ++ back, size: length(p1)}
    127         [c | chd]
    128       end
    129     )
    130   end
    131 end