module Evopop::Crossover

Represents a collection of well known crossover functions.

Public Class Methods

average(candidates, _params) click to toggle source
# File lib/evopop/crossover.rb, line 89
def self.average(candidates, _params)
  new_dna = Evopop::Dna.new(
    candidates[0].dna.min_range,
    candidates[0].dna.max_range,
    candidates[0].dna.min_mutation,
    candidates[0].dna.max_mutation,
    candidates[0].dna.length
  )
  new_dna.dna = []
  (0...candidates[0].dna.length).each do |j|
    # Initialize the dna of the child with the average of the parents' dna.
    new_dna.dna << (candidates[0].dna[j] + candidates[1].dna[j]) / 2.0
  end

  [Evopop::Candidate.new(new_dna)]
end
combine_on_ordinal(dna_a, dna_b, ordinals) click to toggle source
# File lib/evopop/crossover.rb, line 47
def self.combine_on_ordinal(dna_a, dna_b, ordinals)
  dna_a[0..ordinals[0]] + dna_b[(ordinals[0] + 1)..ordinals[1]] + dna_a[(ordinals[1] + 1)..dna_a.length - 1]
end
n_point(candidates, params) click to toggle source

Perform n_point crossover for a pair of candidates. Will output two children from the n_point crossover.

Example: n_point

# File lib/evopop/crossover.rb, line 55
def self.n_point(candidates, params)
  ordinals = params[:ordinals].split(',').sort.collect(&:to_i)

  pdna_a = candidates[0].dna
  pdna_b = candidates[1].dna

  dna_length = candidates[0].dna.length

  cdna_a = []
  cdna_b = []

  old_ordinal = 0
  synchronous = ordinals[0] == 0 ? false : true

  ordinals.each do |i|
    n_ordinal = old_ordinal..i

    cdna_a, cdna_b = CrossoverArray.build_dna_by_synchronous(cdna_a, cdna_b, pdna_a, pdna_b, n_ordinal, synchronous)

    synchronous = !synchronous
    next_ordinal = i + 1

    next if ordinals.last != next_ordinal - 1

    ordinal_range = next_ordinal..(dna_length - 1)
    cdna_a, cdna_b = CrossoverArray.build_dna_by_synchronous(cdna_a, cdna_b, pdna_a, pdna_b, ordinal_range, synchronous)
  end

  [
    Evopop::Candidate.new(cdna_a),
    Evopop::Candidate.new(cdna_b)
  ]
end
one_point(candidates, params) click to toggle source

Perform 1 point crossover for a pair of candidates at the ordinal. en.wikipedia.org/wiki/Crossover_(genetic_algorithm)#One-point_crossover

# File lib/evopop/crossover.rb, line 9
def self.one_point(candidates, params)
  ordinal = params[:ordinal]
  arr_a, arr_b = CrossoverArray.one_point_crossover(candidates[0].dna, candidates[1].dna, ordinal)

  # TODO: Move this to its own class, DnaRange
  min_range = candidates[0].dna.min_range
  max_range = candidates[1].dna.max_range

  # TODO: Move this to its own class, DnaMutationRange
  min_mutation = candidates[1].dna.min_mutation
  max_mutation = candidates[1].dna.max_mutation

  dna_a = Evopop::Dna.create(min_range, max_range, min_mutation, max_mutation, arr_a)
  dna_b = Evopop::Dna.create(min_range, max_range, min_mutation, max_mutation, arr_b)

  # Initialize and assign DNA to children.
  [
    Evopop::Candidate.new(dna_a),
    Evopop::Candidate.new(dna_b)
  ]
end
two_point(candidates, params) click to toggle source

Perform two point crossover over a pair of candidates. Will output two children with genes spliced over the crossover points.

# File lib/evopop/crossover.rb, line 34
def self.two_point(candidates, params)
  # Ordinals should be stored in params as a comma separated list. I.e. "1,2".
  # Make sure to sort.
  ordinals = params[:ordinals].split(',').sort.collect(&:to_i)

  cdna_a, cdna_b = CrossoverArray.two_point_crossover(candidates[0].dna, candidates[1].dna, ordinals)

  [
    Evopop::Candidate.new(cdna_a),
    Evopop::Candidate.new(cdna_b)
  ]
end