All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.ac.sussex.gdsc.smlm.ga.Population Maven / Gradle / Ivy

Go to download

Genome Damage and Stability Centre SMLM Package Software for single molecule localisation microscopy (SMLM)

The newest version!
/*-
 * #%L
 * Genome Damage and Stability Centre SMLM Package
 *
 * Software for single molecule localisation microscopy (SMLM)
 * %%
 * Copyright (C) 2011 - 2023 Alex Herbert
 * %%
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * .
 * #L%
 */

package uk.ac.sussex.gdsc.smlm.ga;

import java.util.ArrayList;
import java.util.List;
import uk.ac.sussex.gdsc.core.logging.TrackProgress;

/**
 * Contains a population of individuals that may crossover and mutate to evolve.
 *
 * 

For simplicity the individuals have one chromosome sequence. * An extension would be to have an Individual class that has many Chromosomes, each is allowed to * crossover with its matching pair and then segregation occurs to new individuals. * * @param the generic type */ public class Population> { private List> individuals; private int populationSize = 500; private int failureLimit = 3; private int iteration; // This introduces a dependency on another uk.ac.sussex.gdsc.smlm package private TrackProgress tracker; /** * Create a population of individuals. * * @param individuals The population of individuals * @throws InvalidPopulationSize if the population is less than 2 */ public Population(List> individuals) { if (individuals == null) { throw new InvalidPopulationSize(0, 1); } checkSize(individuals.size()); this.individuals = individuals; } private static void checkSize(int size) { if (size < 1) { throw new InvalidPopulationSize(0, 1); } } /** * Gets the individuals. * * @return the individuals. */ public List> getIndividuals() { return individuals; } /** * Evolve the population of individuals until convergence of the most fit individual in the * population. * *

The population will grow until the desired population size by recombination of individual * pairs chosen from the population by the selection strategy. Child sequences will be subject to * mutation. The fitness of all the individuals in the new population is evaluated and convergence * checked for the fittest individual. If the initial * *

The process of grow, evaluate, select is repeated until convergence. * *

Note: the subset of individuals selected for the next generation by the selection strategy * will be unchanged (i.e. no mutation). This allows the fittest individuals to remain unchanged. * * @param mutator the mutator * @param recombiner the recombiner * @param fitnessFunction the fitness function * @param selectionStrategy the selection strategy * @param checker the checker * @return The best individual * @throws InvalidPopulationSize if the population is less than 2 (this can occur after selection) */ public Chromosome evolve(Mutator mutator, Recombiner recombiner, FitnessFunction fitnessFunction, SelectionStrategy selectionStrategy, ConvergenceChecker checker) { // Reset the fitness for (final Chromosome c : individuals) { c.setFitness(null); } // Find the best individual grow(selectionStrategy, mutator, recombiner); Chromosome current = evaluateFitness(fitnessFunction); Chromosome previous; boolean converged = false; while (!converged) { previous = current; // Select the best individuals and expand the population if (!select(selectionStrategy)) { current = null; break; } grow(selectionStrategy, mutator, recombiner); // Evaluate the fitness and check convergence current = evaluateFitness(fitnessFunction); converged = checker.converged(previous, current); } if (tracker != null) { tracker.status("Converged [%d]", iteration); } return current; } private void grow(SelectionStrategy selectionStrategy, Mutator mutator, Recombiner recombiner) { iteration++; start("Grow"); if (individuals.size() >= populationSize) { return; } final ArrayList> newIndividuals = new ArrayList<>(populationSize - individuals.size()); // Check for a minimum population size & mutate the individuals to achieve it. // This allows a seed population of 1 to evolve. final int minSize = Math.max(2, individuals.get(0).length()); int target = minSize - individuals.size(); if (target > 0) { if (tracker != null) { tracker.progress(individuals.size(), populationSize); } int next = 0; int fails = 0; while (newIndividuals.size() < target && fails < failureLimit) { final Chromosome c = mutator.mutate(individuals.get(next++ % individuals.size())); if (c != null && !isDuplicate(newIndividuals, c)) { newIndividuals.add(c); fails = 0; if (tracker != null) { tracker.progress(newIndividuals.size() + individuals.size(), populationSize); } } else { fails++; } } // Combine the lists newIndividuals.addAll(individuals); individuals = newIndividuals; if (individuals.size() < 2) { end(); return; // Failed to mutate anything to achieve a breeding population } } // Now breed the population selectionStrategy.initialiseBreeding(individuals); target = populationSize - individuals.size(); int fails = 0; while (newIndividuals.size() < target && fails < failureLimit) { final int previousSize = newIndividuals.size(); // Select two individuals for recombination final ChromosomePair pair = selectionStrategy.next(); final Chromosome[] children = recombiner.cross(pair.c1, pair.c2); if (children != null && children.length != 0) { // New children have been generated so mutate them for (int i = 0; i < children.length && newIndividuals.size() < target; i++) { final Chromosome c = mutator.mutate(children[i]); if (c == null) { continue; } // Ignore duplicates if (isDuplicate(newIndividuals, c)) { continue; } newIndividuals.add(c); } } if (previousSize == newIndividuals.size()) { fails++; } else { fails = 0; if (tracker != null) { tracker.progress(newIndividuals.size() + individuals.size(), populationSize); } } } selectionStrategy.finishBreeding(); // Combine the lists newIndividuals.addAll(individuals); individuals = newIndividuals; end(); } /** * Check for duplicates in the current and new populations. * * @param newIndividuals The new population * @param chromsome The chromosome * @return true if a duplicate */ private boolean isDuplicate(List> newIndividuals, Chromosome chromsome) { final double[] s = chromsome.sequence(); for (final Chromosome i : this.individuals) { if (match(i, s)) { return true; } } for (final Chromosome i : newIndividuals) { if (match(i, s)) { return true; } } return false; } /** * Check if a chromosome matches the sequence. * * @param chromosome The chromosome * @param sequence The sequence * @return True if a match */ private boolean match(Chromosome chromosome, double[] sequence) { final double[] s2 = chromosome.sequence(); for (int i = 0; i < sequence.length; i++) { if (sequence[i] != s2[i]) { return false; } } return true; } /** * Calculate the fitness of the population. * * @param fitnessFunction the fitness function * @return The fittest individual */ private Chromosome evaluateFitness(FitnessFunction fitnessFunction) { start("Score"); Chromosome best = null; T max = null; // Subset only those with no fitness score (the others must be unchanged) final ArrayList> subset = new ArrayList<>(individuals.size()); long count = 0; for (final Chromosome c : individuals) { final T f = c.getFitness(); if (f == null) { subset.add(c); } else { if (tracker != null) { tracker.progress(++count, individuals.size()); } if (f.compareTo(max) < 0) { max = f; best = c; } } } fitnessFunction.initialise(subset); for (final Chromosome c : subset) { final T f = fitnessFunction.fitness(c); c.setFitness(f); if (f != null && f.compareTo(max) < 0) { max = f; best = c; } if (tracker != null) { tracker.progress(++count, individuals.size()); } } fitnessFunction.shutdown(); end(); return best; } /** * Select a subset of the population. * * @param selection The selection strategy * @return True if a valid population was selected (size>=1) */ private boolean select(SelectionStrategy selection) { start("Select"); individuals = selection.select(individuals); end(); return !individuals.isEmpty(); } /** * Get the population size limit to achieve when growing the population. * * @return the populationSize */ public int getPopulationSize() { return populationSize; } /** * Set the population size limit to achieve when growing the population. * * @param populationSize the population size to set */ public void setPopulationSize(int populationSize) { checkSize(populationSize); this.populationSize = populationSize; } /** * Get the number of failed recombinations/mutations to allow before the stopping attempts to grow * the population. * * @return the failure limit */ public int getFailureLimit() { return failureLimit; } /** * Set the number of failed recombinations/mutations to allow before the stopping attempts to grow * the population. * * @param failureLimit the failure limit */ public void setFailureLimit(int failureLimit) { this.failureLimit = failureLimit; } /** * Gets the tracker. * * @return the tracker. */ public TrackProgress getTracker() { return tracker; } /** * Set a tracker to allow the progress to be followed. * * @param tracker the tracker to set */ public void setTracker(TrackProgress tracker) { this.tracker = tracker; } /** * Get the iteration. The iteration is increased each time the population grows as part of the * [grow, evaluate, select] cycle. * * @return the iteration */ public int getIteration() { return iteration; } private void start(String stage) { if (tracker != null) { tracker.status(stage + " [%d]", iteration); tracker.progress(0); } } private void end() { if (tracker != null) { tracker.progress(1); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy