package gdsc.smlm.ga; import java.util.Arrays; import org.apache.commons.math3.random.RandomDataGenerator; import org.apache.commons.math3.random.RandomGenerator; import gdsc.core.utils.Sort; /*----------------------------------------------------------------------------- * GDSC SMLM Software * * Copyright (C) 2015 Alex Herbert * Genome Damage and Stability Centre * University of Sussex, UK * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. *---------------------------------------------------------------------------*/ /** * Recombine sequence by selecting random positions for crossover. */ public class SimpleRecombiner<T extends Comparable<T>> extends Randomiser implements Recombiner<T> { final double fraction; final double meanChildren; /** * @param random * @param fraction * The fraction of the sequence positions to recombine on average * @param meanChildren * The mean number of additional children */ public SimpleRecombiner(RandomDataGenerator random, double fraction, double meanChildren) { super(random); this.fraction = fraction; this.meanChildren = meanChildren; } /** * Crossover the chromosome to form a new sequences. * <p> * The number of children are chosen from a Poisson distribution with an average using the mean children. A minimum * of 1 child is selected to ensure crossover. * <p> * The number of positions are chosen from a Poisson distribution with an average using a fraction of the total * positions. A minimum of 1 crossover position is selected to ensure crossover. * <p> * The positions are then chosen randomly and the new chromosome generated. * * @see gdsc.smlm.ga.Recombiner#cross(gdsc.smlm.ga.Chromosome<T>, gdsc.smlm.ga.Chromosome<T>) */ public Chromosome<T>[] cross(Chromosome<T> chromosome1, Chromosome<T> chromosome2) { int nChildren = 1; if (meanChildren > 0) nChildren = Math.max(1, (int) random.nextPoisson(meanChildren)); @SuppressWarnings("unchecked") Chromosome<T>[] children = new Chromosome[nChildren]; int count = 0; double[] s1 = chromosome1.sequence(); double[] s2 = chromosome2.sequence(); while (count < nChildren) { ChromosomePair<T> pair = recombine(chromosome1, chromosome2, s1, s2); children[count++] = pair.c1; if (count == nChildren) break; children[count++] = pair.c2; } return children; } private ChromosomePair<T> recombine(Chromosome<T> chromosome1, Chromosome<T> chromosome2, double[] s1, double[] s2) { int nCrossovers = 1; if (fraction > 0) nCrossovers = Math.max(1, (int) random.nextPoisson(fraction * chromosome1.length())); // Randomly select positions using a partial Fischer-Yates shuffle int[] positions = new int[s1.length]; for (int i = 0; i < positions.length; i++) positions[i] = i; RandomGenerator ran = random.getRandomGenerator(); for (int i = positions.length, n = nCrossovers; i-- > 1 && n-- > 0;) { int j = (int) (ran.nextInt(i + 1)); int tmp = positions[i]; positions[i] = positions[j]; positions[j] = tmp; } // Reverse the array because the end is random Sort.reverse(positions); positions = Arrays.copyOf(positions, nCrossovers); // Get the positions in order if (nCrossovers != 1) Arrays.sort(positions); int nextSwap = 0; // Create the children by copying the parent, swapping at each crossover position double[] n1 = new double[s1.length]; double[] n2 = new double[n1.length]; for (int i = 0; i < n1.length; i++) { if (positions[nextSwap] == i) { double[] tmp = s1; s1 = s2; s2 = tmp; nextSwap++; // Avoid index out of bounds if (nextSwap == nCrossovers) nextSwap--; } n1[i] = s1[i]; n2[i] = s2[i]; } // Create the new chromosome using the correct parent, i.e. // If the first swap position was at the start then reverse them. Chromosome<T> c1 = (positions[0] == 0) ? chromosome2 : chromosome1; Chromosome<T> c2 = (positions[0] == 0) ? chromosome1 : chromosome2; c1 = c1.newChromosome(n1); c2 = c2.newChromosome(n2); // Ensure the child order is random return (ran.nextDouble() < 0.5) ? new ChromosomePair<T>(c1, c2) : new ChromosomePair<T>(c2, c1); } }