package gdsc.smlm.ga; import org.apache.commons.math3.random.RandomDataGenerator; /*----------------------------------------------------------------------------- * 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. *---------------------------------------------------------------------------*/ /** * Mutates the sequence by selecting random positions and random shifts. */ public class SimpleMutator<T extends Comparable<T>> extends Randomiser implements Mutator<T> { final double fraction; private boolean override = false; private double[] stepSize, lower, upper; private int[] positions; private int positionsCount; /** * @param random * @param fraction * The fraction of the sequence positions to mutate on average */ public SimpleMutator(RandomDataGenerator random, double fraction) { super(random); this.fraction = fraction; } /** * Override the mutation parameters that are obtained from the Chromosome interface. * The arrays must match the fixed size of the Chromosome sequences to be mutated. * <p> * All settings are overridden even if null arrays are passed for some arguments. * * @param stepSize * The mutation step size (must not be null) * @param lower * The lower limit for the sequence positions (can be null) * @param upper * The upper limit for the sequence positions (can be null) * @throws IllegalArgumentException * if the input limit arrays are of different lengths to the step size */ public void overrideChromosomeSettings(double[] stepSize, double[] lower, double[] upper) { if (stepSize == null) throw new IllegalArgumentException("Step size must not be null"); if (lower != null && lower.length != stepSize.length) throw new IllegalArgumentException("Lower limit must be the same length as the step size"); if (upper != null && upper.length != stepSize.length) throw new IllegalArgumentException("Upper limit must be the same length as the step size"); this.stepSize = stepSize; this.lower = lower; this.upper = upper; getStepPositions(stepSize); override = true; // (stepSize != null || lower != null || upper != null); } /** * Determine the positions that have a step size greater than zero, i.e. can be mutated * * @param step * The step sizes for each sequence position */ private void getStepPositions(double[] step) { positions = new int[step.length]; positionsCount = 0; for (int i = 0; i < step.length; i++) { if (step[i] > 0) positions[positionsCount++] = i; } } /** * Mutates the chromosome to form a new sequence. * <p> * The number of positions are chosen from a Poisson distribution with an average using a fraction of the total * positions. The positions are then chosen randomly. Note that the same position may be chosen multiple times. The * random shifts for each mutation are taken from a Gaussian using the chromosome mutation step range as the * standard deviation. Set step size to zero for no mutation at a position. * * @see gdsc.smlm.ga.Mutator#mutate(gdsc.smlm.ga.Chromosome) */ public Chromosome<T> mutate(Chromosome<T> chromosome) { final double[] sequence = chromosome.sequence().clone(); final double mean = fraction * chromosome.length(); if (mean > 0) { int count = (int) random.nextPoisson(mean); final double[] step, min, max; // Only override if the length is correct if (override && stepSize.length == chromosome.length()) { step = stepSize; min = lower; max = upper; } else { step = chromosome.mutationStepRange(); min = chromosome.lowerLimit(); max = chromosome.upperLimit(); getStepPositions(step); } if (positionsCount == 0) return chromosome.newChromosome(sequence); while (count-- > 0) { final int i = positions[random.getRandomGenerator().nextInt(positionsCount)]; sequence[i] = random.nextGaussian(sequence[i], step[i]); // Check limits if (min != null) { if (sequence[i] < min[i]) sequence[i] = min[i]; } if (max != null) { if (sequence[i] > max[i]) sequence[i] = max[i]; } } } return chromosome.newChromosome(sequence); } }